Преглед на файлове

env updating and GHA cleanup on destroy

Alexander Belanger преди 5 години
родител
ревизия
4cbf6557f6
променени са 3 файла, в които са добавени 358 реда и са изтрити 3 реда
  1. 123 1
      internal/integrations/ci/actions/actions.go
  2. 83 1
      server/api/deploy_handler.go
  3. 152 1
      server/api/release_handler.go

+ 123 - 1
internal/integrations/ci/actions/actions.go

@@ -87,6 +87,43 @@ func (g *GithubActions) Setup() (string, error) {
 	return g.commitGithubFile(client, g.getPorterYMLFileName(), fileBytes)
 }
 
+func (g *GithubActions) Cleanup() error {
+	client, err := g.getClient()
+
+	if err != nil {
+		return err
+	}
+
+	// get the repository to find the default branch
+	repo, _, err := client.Repositories.Get(
+		context.TODO(),
+		g.GitRepoOwner,
+		g.GitRepoName,
+	)
+
+	if err != nil {
+		return err
+	}
+
+	g.defaultBranch = repo.GetDefaultBranch()
+
+	// delete the webhook token secret
+	err = g.deleteGithubSecret(client, g.getWebhookSecretName())
+
+	if err != nil {
+		return err
+	}
+
+	// delete the env secret
+	err = g.deleteGithubSecret(client, g.getBuildEnvSecretName())
+
+	if err != nil {
+		return err
+	}
+
+	return g.deleteGithubFile(client, g.getPorterYMLFileName())
+}
+
 type GithubActionYAMLStep struct {
 	Name string `yaml:"name,omitempty"`
 	ID   string `yaml:"id,omitempty"`
@@ -209,7 +246,32 @@ func (g *GithubActions) createGithubSecret(
 	// write the secret to the repo
 	_, err = client.Actions.CreateOrUpdateRepoSecret(context.TODO(), g.GitRepoOwner, g.GitRepoName, encryptedSecret)
 
-	return nil
+	return err
+}
+
+func (g *GithubActions) deleteGithubSecret(
+	client *github.Client,
+	secretName string,
+) error {
+	// delete the secret from the repo
+	_, err := client.Actions.DeleteRepoSecret(
+		context.TODO(),
+		g.GitRepoOwner,
+		g.GitRepoName,
+		secretName,
+	)
+
+	return err
+}
+
+func (g *GithubActions) CreateEnvSecret() error {
+	client, err := g.getClient()
+
+	if err != nil {
+		return err
+	}
+
+	return g.createEnvSecret(client)
 }
 
 func (g *GithubActions) createEnvSecret(client *github.Client) error {
@@ -253,11 +315,26 @@ func (g *GithubActions) commitGithubFile(
 	contents []byte,
 ) (string, error) {
 	filepath := ".github/workflows/" + filename
+	sha := ""
+
+	// get contents of a file if it exists
+	fileData, _, _, _ := client.Repositories.GetContents(
+		context.TODO(),
+		g.GitRepoOwner,
+		g.GitRepoName,
+		filepath,
+		&github.RepositoryContentGetOptions{},
+	)
+
+	if fileData != nil {
+		sha = *fileData.SHA
+	}
 
 	opts := &github.RepositoryContentFileOptions{
 		Message: github.String(fmt.Sprintf("Create %s file", filename)),
 		Content: contents,
 		Branch:  github.String(g.defaultBranch),
+		SHA:     &sha,
 		Committer: &github.CommitAuthor{
 			Name:  github.String("Porter Bot"),
 			Email: github.String("contact@getporter.dev"),
@@ -278,3 +355,48 @@ func (g *GithubActions) commitGithubFile(
 
 	return *resp.Commit.SHA, nil
 }
+
+func (g *GithubActions) deleteGithubFile(
+	client *github.Client,
+	filename string,
+) error {
+	filepath := ".github/workflows/" + filename
+	sha := ""
+
+	// get contents of a file if it exists
+	fileData, _, _, _ := client.Repositories.GetContents(
+		context.TODO(),
+		g.GitRepoOwner,
+		g.GitRepoName,
+		filepath,
+		&github.RepositoryContentGetOptions{},
+	)
+
+	if fileData != nil {
+		sha = *fileData.SHA
+	}
+
+	opts := &github.RepositoryContentFileOptions{
+		Message: github.String(fmt.Sprintf("Delete %s file", filename)),
+		Branch:  github.String(g.defaultBranch),
+		SHA:     &sha,
+		Committer: &github.CommitAuthor{
+			Name:  github.String("Porter Bot"),
+			Email: github.String("contact@getporter.dev"),
+		},
+	}
+
+	_, _, err := client.Repositories.DeleteFile(
+		context.TODO(),
+		g.GitRepoOwner,
+		g.GitRepoName,
+		filepath,
+		opts,
+	)
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}

+ 83 - 1
server/api/deploy_handler.go

@@ -6,12 +6,15 @@ import (
 	"net/http"
 	"net/url"
 	"strconv"
+	"strings"
 
 	"github.com/go-chi/chi"
 	"github.com/porter-dev/porter/internal/forms"
 	"github.com/porter-dev/porter/internal/helm"
 	"github.com/porter-dev/porter/internal/helm/loader"
+	"github.com/porter-dev/porter/internal/integrations/ci/actions"
 	"github.com/porter-dev/porter/internal/models"
+	"gopkg.in/yaml.v2"
 )
 
 // HandleDeployTemplate triggers a chart deployment from a template
@@ -166,6 +169,13 @@ func (app *App) HandleDeployTemplate(w http.ResponseWriter, r *http.Request) {
 func (app *App) HandleUninstallTemplate(w http.ResponseWriter, r *http.Request) {
 	name := chi.URLParam(r, "name")
 
+	vals, err := url.ParseQuery(r.URL.RawQuery)
+
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
+		return
+	}
+
 	form := &forms.GetReleaseForm{
 		ReleaseForm: &forms.ReleaseForm{
 			Form: &helm.Form{
@@ -187,11 +197,83 @@ func (app *App) HandleUninstallTemplate(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
-	_, err = agent.UninstallChart(name)
+	resp, err := agent.UninstallChart(name)
 	if err != nil {
 		return
 	}
 
+	// update the github actions env if the release exists and is built from source
+	if cName := resp.Release.Chart.Metadata.Name; cName == "job" || cName == "web" || cName == "worker" {
+		clusterID, err := strconv.ParseUint(vals["cluster_id"][0], 10, 64)
+
+		if err != nil {
+			app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
+				Code:   ErrReleaseReadData,
+				Errors: []string{"release not found"},
+			}, w)
+		}
+
+		release, err := app.Repo.Release.ReadRelease(uint(clusterID), name, resp.Release.Namespace)
+		gitAction := release.GitActionConfig
+
+		if release != nil && gitAction.ID != 0 {
+			// parse env into build env
+			cEnv := &ContainerEnvConfig{}
+			rawValues, err := yaml.Marshal(resp.Release.Config)
+
+			if err != nil {
+				app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
+					Code:   ErrReleaseReadData,
+					Errors: []string{"could not get values of previous revision"},
+				}, w)
+			}
+
+			yaml.Unmarshal(rawValues, cEnv)
+
+			gr, err := app.Repo.GitRepo.ReadGitRepo(gitAction.GitRepoID)
+
+			if err != nil {
+				app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
+					Code:   ErrReleaseReadData,
+					Errors: []string{"github repo integration not found"},
+				}, w)
+			}
+
+			repoSplit := strings.Split(gitAction.GitRepo, "/")
+
+			projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
+
+			if err != nil || projID == 0 {
+				app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+				return
+			}
+
+			gaRunner := &actions.GithubActions{
+				GitIntegration: gr,
+				GitRepoName:    repoSplit[1],
+				GitRepoOwner:   repoSplit[0],
+				Repo:           *app.Repo,
+				GithubConf:     app.GithubProjectConf,
+				WebhookToken:   release.WebhookToken,
+				ProjectID:      uint(projID),
+				ReleaseName:    name,
+				DockerFilePath: gitAction.DockerfilePath,
+				FolderPath:     gitAction.FolderPath,
+				ImageRepoURL:   gitAction.ImageRepoURI,
+				BuildEnv:       cEnv.Container.Env.Normal,
+			}
+
+			err = gaRunner.Cleanup()
+
+			if err != nil {
+				app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
+					Code:   ErrReleaseReadData,
+					Errors: []string{"could not remove github action"},
+				}, w)
+			}
+		}
+	}
+
 	w.WriteHeader(http.StatusOK)
 	return
 }

+ 152 - 1
server/api/release_handler.go

@@ -17,9 +17,11 @@ import (
 	"github.com/porter-dev/porter/internal/forms"
 	"github.com/porter-dev/porter/internal/helm"
 	"github.com/porter-dev/porter/internal/helm/grapher"
+	"github.com/porter-dev/porter/internal/integrations/ci/actions"
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/repository"
 	segment "gopkg.in/segmentio/analytics-go.v3"
+	"gopkg.in/yaml.v2"
 )
 
 // Enumeration of release API error codes, represented as int64
@@ -469,6 +471,14 @@ func (app *App) HandleGetReleaseToken(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
+type ContainerEnvConfig struct {
+	Container struct {
+		Env struct {
+			Normal map[string]string `yaml:"normal"`
+		} `yaml:"env"`
+	} `yaml:"container"`
+}
+
 // HandleUpgradeRelease upgrades a release with new values.yaml
 func (app *App) HandleUpgradeRelease(w http.ResponseWriter, r *http.Request) {
 	projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
@@ -532,7 +542,7 @@ func (app *App) HandleUpgradeRelease(w http.ResponseWriter, r *http.Request) {
 		Registries: registries,
 	}
 
-	_, err = agent.UpgradeRelease(conf, form.Values, app.DOConf)
+	rel, err := agent.UpgradeRelease(conf, form.Values, app.DOConf)
 
 	if err != nil {
 		app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
@@ -543,6 +553,63 @@ func (app *App) HandleUpgradeRelease(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	// update the github actions env if the release exists and is built from source
+	if cName := rel.Chart.Metadata.Name; cName == "job" || cName == "web" || cName == "worker" {
+		clusterID, err := strconv.ParseUint(vals["cluster_id"][0], 10, 64)
+
+		if err != nil {
+			app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
+				Code:   ErrReleaseReadData,
+				Errors: []string{"release not found"},
+			}, w)
+		}
+
+		release, err := app.Repo.Release.ReadRelease(uint(clusterID), name, rel.Namespace)
+		gitAction := release.GitActionConfig
+
+		if release != nil && gitAction.ID != 0 {
+			// parse env into build env
+			cEnv := &ContainerEnvConfig{}
+
+			yaml.Unmarshal([]byte(form.Values), cEnv)
+
+			gr, err := app.Repo.GitRepo.ReadGitRepo(gitAction.GitRepoID)
+
+			if err != nil {
+				app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
+					Code:   ErrReleaseReadData,
+					Errors: []string{"github repo integration not found"},
+				}, w)
+			}
+
+			repoSplit := strings.Split(gitAction.GitRepo, "/")
+
+			gaRunner := &actions.GithubActions{
+				GitIntegration: gr,
+				GitRepoName:    repoSplit[1],
+				GitRepoOwner:   repoSplit[0],
+				Repo:           *app.Repo,
+				GithubConf:     app.GithubProjectConf,
+				WebhookToken:   release.WebhookToken,
+				ProjectID:      uint(projID),
+				ReleaseName:    name,
+				DockerFilePath: gitAction.DockerfilePath,
+				FolderPath:     gitAction.FolderPath,
+				ImageRepoURL:   gitAction.ImageRepoURI,
+				BuildEnv:       cEnv.Container.Env.Normal,
+			}
+
+			err = gaRunner.CreateEnvSecret()
+
+			if err != nil {
+				app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
+					Code:   ErrReleaseReadData,
+					Errors: []string{"could not update github secret"},
+				}, w)
+			}
+		}
+	}
+
 	w.WriteHeader(http.StatusOK)
 }
 
@@ -700,6 +767,90 @@ func (app *App) HandleRollbackRelease(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	// get the full release data for GHA updating
+	rel, err := agent.GetRelease(form.Name, form.Revision)
+
+	if err != nil {
+		app.sendExternalError(err, http.StatusNotFound, HTTPError{
+			Code:   ErrReleaseReadData,
+			Errors: []string{"release not found"},
+		}, w)
+
+		return
+	}
+
+	// update the github actions env if the release exists and is built from source
+	if cName := rel.Chart.Metadata.Name; cName == "job" || cName == "web" || cName == "worker" {
+		clusterID, err := strconv.ParseUint(vals["cluster_id"][0], 10, 64)
+
+		if err != nil {
+			app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
+				Code:   ErrReleaseReadData,
+				Errors: []string{"release not found"},
+			}, w)
+		}
+
+		release, err := app.Repo.Release.ReadRelease(uint(clusterID), name, rel.Namespace)
+		gitAction := release.GitActionConfig
+
+		if release != nil && gitAction.ID != 0 {
+			// parse env into build env
+			cEnv := &ContainerEnvConfig{}
+			rawValues, err := yaml.Marshal(rel.Config)
+
+			if err != nil {
+				app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
+					Code:   ErrReleaseReadData,
+					Errors: []string{"could not get values of previous revision"},
+				}, w)
+			}
+
+			yaml.Unmarshal(rawValues, cEnv)
+
+			gr, err := app.Repo.GitRepo.ReadGitRepo(gitAction.GitRepoID)
+
+			if err != nil {
+				app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
+					Code:   ErrReleaseReadData,
+					Errors: []string{"github repo integration not found"},
+				}, w)
+			}
+
+			repoSplit := strings.Split(gitAction.GitRepo, "/")
+
+			projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
+
+			if err != nil || projID == 0 {
+				app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+				return
+			}
+
+			gaRunner := &actions.GithubActions{
+				GitIntegration: gr,
+				GitRepoName:    repoSplit[1],
+				GitRepoOwner:   repoSplit[0],
+				Repo:           *app.Repo,
+				GithubConf:     app.GithubProjectConf,
+				WebhookToken:   release.WebhookToken,
+				ProjectID:      uint(projID),
+				ReleaseName:    name,
+				DockerFilePath: gitAction.DockerfilePath,
+				FolderPath:     gitAction.FolderPath,
+				ImageRepoURL:   gitAction.ImageRepoURI,
+				BuildEnv:       cEnv.Container.Env.Normal,
+			}
+
+			err = gaRunner.CreateEnvSecret()
+
+			if err != nil {
+				app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
+					Code:   ErrReleaseReadData,
+					Errors: []string{"could not update github secret"},
+				}, w)
+			}
+		}
+	}
+
 	w.WriteHeader(http.StatusOK)
 }