diff --git a/internals/cron/cloudfoundry.go b/internals/cron/cloudfoundry.go
new file mode 100644
index 0000000..f616856
--- /dev/null
+++ b/internals/cron/cloudfoundry.go
@@ -0,0 +1,69 @@
+package cron
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+)
+
+func deploy(apikey, org, space, resourceGroup, region string) error {
+ githubUser := os.Getenv("GITHUB_USER")
+ githubToken := os.Getenv("GITHUB_TOKEN")
+ grantClusterURL := os.Getenv("GRANT_CLUSTER_REPO_URL")
+
+ if err := cmd("ibmcloud",
+ "login", "--apikey", apikey, "-a", "https://cloud.ibm.com", "-r", region); err != nil {
+ return err
+ }
+ if err := cmd("ibmcloud",
+ "target", "-o", org, "-s", space, "-g", resourceGroup); err != nil {
+ return err
+ }
+ if err := cmd("git",
+ "clone", fmt.Sprintf("https://%s:%s@%s.git", githubUser, githubToken, grantClusterURL)); err != nil {
+ return err
+ }
+
+ defer cleanup("grant-cluster")
+
+ if err := cmd("./grant-cluster/scripts/deploy-app.sh"); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Cleanup folder
+func cleanup(filepaths ...string) error {
+ for _, filepath := range filepaths {
+ fi, err := os.Stat(filepath)
+ if err != nil {
+ return err
+ }
+ mode := fi.Mode()
+ if mode.IsDir() {
+ if err := os.RemoveAll(filepath); err != nil {
+ return err
+ }
+ } else if mode.IsRegular() {
+ if err := os.Remove(filepath); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func cmd(name string, args ...string) error {
+ cmd := exec.Command(name, args...)
+
+ cmd.Stdin = os.Stdin
+ cmd.Stderr = os.Stderr
+
+ if output, err := cmd.Output(); err != nil {
+ return err
+ } else {
+ fmt.Printf("%s\n", output)
+ }
+ return nil
+}
diff --git a/internals/cron/cron.go b/internals/cron/cron.go
index 581cc73..84a1286 100644
--- a/internals/cron/cron.go
+++ b/internals/cron/cron.go
@@ -1,9 +1,7 @@
package cron
import (
- "bytes"
"fmt"
- "html/template"
"log"
"math/rand"
"os"
@@ -277,23 +275,18 @@ func checkCloudant() {
if err := notification.Email("IBMCloud Kubernetes Admin Schedule executed", emailBody, notifyEmails...); err != nil {
log.Println("error sending email")
}
- }
- }
-}
-func getEmailBody(data EmailData) (string, error) {
- tmpl, err := template.ParseFiles("templates/email.gohtml")
- if err != nil {
- log.Println("could not parse file", err)
- return "", err
- }
- htmlTemplate := template.Must(tmpl, err)
- buf := new(bytes.Buffer)
+ // if its a workshop deploy cloud foundry and update github issue
+ if !schedule.IsWorkshop {
+ continue
+ }
- if err := htmlTemplate.Execute(buf, data); err != nil {
- log.Println("could not parse file", err)
- return "", err
+ setEnvs(accountID, schedule)
+ apikey := session.GetAPIKey(accountID)
+ org, space, region := "", "", ""
+ if err != deploy(apikey, org, space, schedule.ResourceGroupName, region); err != nil {
+ notification.EmailAdmin("failed deploying cloud foundry app", "
Cloud foundry app failed to deploy
")
+ }
+ }
}
-
- return buf.String(), nil
}
diff --git a/internals/cron/email.go b/internals/cron/email.go
new file mode 100644
index 0000000..016b7ac
--- /dev/null
+++ b/internals/cron/email.go
@@ -0,0 +1,24 @@
+package cron
+
+import (
+ "bytes"
+ "html/template"
+ "log"
+)
+
+func getEmailBody(data EmailData) (string, error) {
+ tmpl, err := template.ParseFiles("templates/email.gohtml")
+ if err != nil {
+ log.Println("could not parse file", err)
+ return "", err
+ }
+ htmlTemplate := template.Must(tmpl, err)
+ buf := new(bytes.Buffer)
+
+ if err := htmlTemplate.Execute(buf, data); err != nil {
+ log.Println("could not parse file", err)
+ return "", err
+ }
+
+ return buf.String(), nil
+}
diff --git a/internals/cron/env.go b/internals/cron/env.go
new file mode 100644
index 0000000..6d9bfc3
--- /dev/null
+++ b/internals/cron/env.go
@@ -0,0 +1,30 @@
+package cron
+
+import (
+ "os"
+
+ "github.com/moficodes/ibmcloud-kubernetes-admin/pkg/ibmcloud"
+)
+
+func setEnvs(accountID string, schedule ibmcloud.Schedule) error {
+
+ if err := os.Setenv("EVENT_NAME", schedule.EventName); err != nil {
+ return err
+ }
+ if err := os.Setenv("PASSWORD", schedule.Password); err != nil {
+ return err
+ }
+ if err := os.Setenv("RESOURCE_GROUP_NAME", schedule.ResourceGroupName); err != nil {
+ return err
+ }
+ if err := os.Setenv("ACCESS_GROUP_NAME", schedule.AccessGroupName); err != nil {
+ return err
+ }
+ if err := os.Setenv("APP_HOSTNAME", schedule.EventName); err != nil {
+ return err
+ }
+ if err := os.Setenv("ACCOUNT", accountID); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/internals/cron/github.go b/internals/cron/github.go
new file mode 100644
index 0000000..8d6b313
--- /dev/null
+++ b/internals/cron/github.go
@@ -0,0 +1,25 @@
+package cron
+
+import (
+ "encoding/base64"
+ "os"
+
+ "github.com/moficodes/ibmcloud-kubernetes-admin/pkg/notification"
+)
+
+func createComment(issue, comment string) error {
+ apiEndpoint := os.Getenv("GITHUB_API_ENDPOINT")
+ repo := os.Getenv("REPO")
+ owner := os.Getenv("OWNER")
+ githubUser := os.Getenv("GITHUB_USER")
+ githubToken := os.Getenv("GITHUB_TOKEN")
+ token := "Basic " + base64Encode(githubUser+":"+githubToken)
+ if err := notification.CreateComment(token, apiEndpoint, owner, repo, issue, comment); err != nil {
+ return err
+ }
+ return nil
+}
+
+func base64Encode(data string) string {
+ return base64.StdEncoding.EncodeToString([]byte(data))
+}
diff --git a/pkg/ibmcloud/api.go b/pkg/ibmcloud/api.go
index d186736..9399c62 100644
--- a/pkg/ibmcloud/api.go
+++ b/pkg/ibmcloud/api.go
@@ -61,12 +61,6 @@ const (
const basicAuth = "Basic Yng6Yng="
-var client *http.Client
-
-func init() {
- client = &http.Client{Timeout: time.Duration(150 * time.Second)}
-}
-
//// useful for loagging
// bodyBytes, err := ioutil.ReadAll(resp.Body)
// if err != nil {
diff --git a/pkg/ibmcloud/cloudant.go b/pkg/ibmcloud/cloudant.go
index e00f281..b310a08 100644
--- a/pkg/ibmcloud/cloudant.go
+++ b/pkg/ibmcloud/cloudant.go
@@ -39,6 +39,19 @@ func setupDB(dbName string) error {
return err
}
+func GetAPIKey(accountID string) (string, err) {
+ dbName := "db-" + accountID
+ return getAPIKey(dbName)
+}
+
+func getAPIKey(dbName string) (string, error) {
+ apiKey, err := getAPIKey(dbName)
+ if err != nil {
+ return "", err
+ }
+ return apiKey.APIKey, nil
+}
+
func CheckAPIKey(accountID string) error {
dbName := "db-" + accountID
return checkExistingAPIKey(dbName)
diff --git a/pkg/ibmcloud/request.go b/pkg/ibmcloud/request.go
index 5430ae9..cc3e180 100644
--- a/pkg/ibmcloud/request.go
+++ b/pkg/ibmcloud/request.go
@@ -9,6 +9,7 @@ import (
"net/http"
"net/url"
"strings"
+ "time"
)
func handleRequest(request *http.Request, header map[string]string, query map[string]string, res interface{}) error {
@@ -23,6 +24,8 @@ func handleRequest(request *http.Request, header map[string]string, query map[st
request.URL.RawQuery = q.Encode()
+ client := &http.Client{Timeout: time.Duration(150 * time.Second)}
+
resp, err := client.Do(request)
if err != nil {
return err
diff --git a/pkg/ibmcloud/session.go b/pkg/ibmcloud/session.go
index 6da8fbe..b2dd729 100644
--- a/pkg/ibmcloud/session.go
+++ b/pkg/ibmcloud/session.go
@@ -218,6 +218,19 @@ func (s *Session) CheckAPIKey(accountID string) error {
return CheckAPIKey(accountID)
}
+func (s *Session) GetAPIKey(accountID string) (string, error) {
+ if !s.IsValid() {
+ log.Println("Access token expired.")
+ token, err := upgradeToken(endpoints.TokenEndpoint, s.Token.RefreshToken, accountID)
+ if err != nil {
+ return err
+ }
+ log.Println("Token Refreshed.")
+ s.Token = token
+ }
+ return GetAPIKey(accountID)
+}
+
func (s *Session) UpdateAPIKey(apiKey, accountID string) error {
if !s.IsValid() {
log.Println("Access token expired.")
diff --git a/pkg/ibmcloud/types.go b/pkg/ibmcloud/types.go
index 0fd99d6..f308c76 100644
--- a/pkg/ibmcloud/types.go
+++ b/pkg/ibmcloud/types.go
@@ -417,16 +417,21 @@ type CreateClusterRequest struct {
}
type Schedule struct {
- ID string `json:"_id" mapstructure:"_id"`
- Rev string `json:"_rev" mapstructure:"_rev"`
- CreateAt int `json:"createAt"`
- DestroyAt int `json:"destroyAt"`
- Status string `json:"status"`
- Tags string `json:"tags"`
- Count string `json:"count"`
- CreateRequest CreateClusterRequest `json:"createRequest"`
- Clusters []string `json:"clusters"`
- NotifyEmails []string `json:"notifyEmails"`
+ ID string `json:"_id" mapstructure:"_id"`
+ Rev string `json:"_rev" mapstructure:"_rev"`
+ CreateAt int `json:"createAt"`
+ DestroyAt int `json:"destroyAt"`
+ Status string `json:"status"`
+ Tags string `json:"tags"`
+ Count string `json:"count"`
+ CreateRequest CreateClusterRequest `json:"createRequest"`
+ Clusters []string `json:"clusters"`
+ NotifyEmails []string `json:"notifyEmails"`
+ EventName string `json:"eventName"`
+ Password string `json:"password"`
+ ResourceGroupName string `json:"resourceGroupName"`
+ AccessGroupName string `json:"accessGroupName"`
+ IsWorkshop bool `json:"isWorkshop"`
}
type Vlan struct {
diff --git a/pkg/notification/github.go b/pkg/notification/github.go
new file mode 100644
index 0000000..4b35109
--- /dev/null
+++ b/pkg/notification/github.go
@@ -0,0 +1,94 @@
+package notification
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "log"
+ "net/http"
+ "time"
+)
+
+const githubBaseURL = "https://api.github.ibm.com"
+
+func CreateComment(token, apiEndpoint, owner, repo, issue, comment string) error {
+ endpoint := fmt.Sprintf("%s/repos/%s/%s/issues/%s/comments", githubBaseURL, owner, repo, issue)
+
+ header := map[string]string{
+ "Authorization": token,
+ }
+
+ type Comment struct {
+ Body string `json:"body"`
+ }
+
+ c := &Comment{
+ Body: comment,
+ }
+
+ jsonValue, err := json.Marshal(c)
+ if err != nil {
+ return err
+ }
+
+ var res interface{}
+
+ if err := postBody(endpoint, header, nil, jsonValue, res); err != nil {
+ return err
+ }
+ return nil
+}
+
+// postBody makes a post request with json body
+func postBody(endpoint string, header, query map[string]string, jsonValue []byte, res interface{}) error {
+ request, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBuffer(jsonValue))
+ if err != nil {
+ return err
+ }
+
+ return handleRequest(request, header, query, res)
+}
+
+func handleRequest(request *http.Request, header map[string]string, query map[string]string, res interface{}) error {
+ for key, value := range header {
+ request.Header.Add(key, value)
+ }
+
+ q := request.URL.Query()
+ for key, value := range query {
+ q.Add(key, value)
+ }
+
+ request.URL.RawQuery = q.Encode()
+
+ client := &http.Client{Timeout: time.Duration(150 * time.Second)}
+
+ resp, err := client.Do(request)
+ if err != nil {
+ return err
+ }
+
+ defer resp.Body.Close()
+
+ if !(resp.StatusCode >= 200 && resp.StatusCode < 300) {
+ json, err := json.Marshal(resp.Body)
+ if err != nil {
+ log.Println(err)
+ }
+ return errors.New(string(json))
+ }
+
+ // b, _ := ioutil.ReadAll(resp.Body)
+ // log.Println(string(b))
+ // This was a delete request and was successful.
+ // no need to try decode the body.
+ if resp.StatusCode == 204 {
+ return nil
+ }
+
+ if err = json.NewDecoder(resp.Body).Decode(&res); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/scripts/login.sh b/scripts/login.sh
new file mode 100755
index 0000000..d98f764
--- /dev/null
+++ b/scripts/login.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+ibmcloud login --apikey $APIKEY -a https://cloud.ibm.com -r us-south
+
+ibmcloud target -o advowork@us.ibm.com -s dev
+
+ibmcloud account list
\ No newline at end of file
diff --git a/templates/message.gotmpl b/templates/message.gotmpl
new file mode 100644
index 0000000..b7d2cb4
--- /dev/null
+++ b/templates/message.gotmpl
@@ -0,0 +1,13 @@
+"URL: https://{{ .EventName }}.mybluemix.net
+
+Key: `{{ .Password }}`
+
+Region: {{ .CreateClusterRequest.ClusterRequest.DataCenter }}
+Clusters: {{ .Count }}
+Workers: {{ .CreateClusterRequest.ClusterRequest.WorkerNum }} x {{ .CreateClusterRequest.ClusterRequest.MachineType }}
+K8s Version: {{ .CreateClusterRequest.ClusterRequest.MasterVersion }}
+
+## Note
+- Please be sure to click: `Prefill Cache` button on the URL before your lab
+- Don't forget you have your https://{{ .EventName }}.mybluemix.net/admin to see the status of the clusters too
+- If you need a `cloudshell` you can use https://shell.cloud.ibm.com/. It should be attached to the IBMid, that the student will have created for your workshop. If you have issues with it though please ask questions in #cloudshell-users, we do not control it.
\ No newline at end of file