Skip to content

Commit

Permalink
first pass at auto deploy cloud foundry
Browse files Browse the repository at this point in the history
  • Loading branch information
moficodes committed May 27, 2020
1 parent 85311dc commit d81d321
Show file tree
Hide file tree
Showing 13 changed files with 316 additions and 34 deletions.
69 changes: 69 additions & 0 deletions internals/cron/cloudfoundry.go
Original file line number Diff line number Diff line change
@@ -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
}
29 changes: 11 additions & 18 deletions internals/cron/cron.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package cron

import (
"bytes"
"fmt"
"html/template"
"log"
"math/rand"
"os"
Expand Down Expand Up @@ -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", "<h1>Cloud foundry app failed to deploy</h1>")
}
}
}

return buf.String(), nil
}
24 changes: 24 additions & 0 deletions internals/cron/email.go
Original file line number Diff line number Diff line change
@@ -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
}
30 changes: 30 additions & 0 deletions internals/cron/env.go
Original file line number Diff line number Diff line change
@@ -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
}
25 changes: 25 additions & 0 deletions internals/cron/github.go
Original file line number Diff line number Diff line change
@@ -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))
}
6 changes: 0 additions & 6 deletions pkg/ibmcloud/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
13 changes: 13 additions & 0 deletions pkg/ibmcloud/cloudant.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions pkg/ibmcloud/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand Down
13 changes: 13 additions & 0 deletions pkg/ibmcloud/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand Down
25 changes: 15 additions & 10 deletions pkg/ibmcloud/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
94 changes: 94 additions & 0 deletions pkg/notification/github.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit d81d321

Please sign in to comment.