|
|
@@ -2,6 +2,7 @@ package release
|
|
|
|
|
|
import (
|
|
|
"encoding/json"
|
|
|
+ "errors"
|
|
|
"fmt"
|
|
|
"net/http"
|
|
|
"strings"
|
|
|
@@ -19,6 +20,7 @@ import (
|
|
|
"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/integrations/ci/gitlab"
|
|
|
"github.com/porter-dev/porter/internal/models"
|
|
|
"github.com/porter-dev/porter/internal/oauth"
|
|
|
"github.com/porter-dev/porter/internal/registry"
|
|
|
@@ -46,7 +48,6 @@ func NewCreateReleaseHandler(
|
|
|
|
|
|
func (c *CreateReleaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
|
user, _ := r.Context().Value(types.UserScope).(*models.User)
|
|
|
- proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
|
|
|
cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
|
|
|
namespace := r.Context().Value(types.NamespaceScope).(string)
|
|
|
operationID := oauth.CreateRandomState()
|
|
|
@@ -137,7 +138,7 @@ func (c *CreateReleaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- release, err := createReleaseFromHelmRelease(c.Config(), cluster.ProjectID, cluster.ID, helmRelease)
|
|
|
+ release, err := CreateAppReleaseFromHelmRelease(c.Config(), cluster.ProjectID, cluster.ID, 0, helmRelease)
|
|
|
|
|
|
if err != nil {
|
|
|
c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
|
|
|
@@ -163,34 +164,43 @@ func (c *CreateReleaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if request.GithubActionConfig != nil {
|
|
|
+ if request.BuildConfig != nil {
|
|
|
+ _, err = createBuildConfig(c.Config(), release, request.BuildConfig)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if request.GitActionConfig != nil {
|
|
|
_, _, err := createGitAction(
|
|
|
c.Config(),
|
|
|
- proj,
|
|
|
user.ID,
|
|
|
cluster.ProjectID,
|
|
|
cluster.ID,
|
|
|
- request.GithubActionConfig,
|
|
|
+ request.GitActionConfig,
|
|
|
request.Name,
|
|
|
namespace,
|
|
|
release,
|
|
|
)
|
|
|
|
|
|
if err != nil {
|
|
|
- c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
|
|
|
- return
|
|
|
+ unwrappedErr := errors.Unwrap(err)
|
|
|
+
|
|
|
+ if unwrappedErr != nil {
|
|
|
+ if errors.Is(unwrappedErr, actions.ErrProtectedBranch) {
|
|
|
+ c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusConflict))
|
|
|
+ } else if errors.Is(unwrappedErr, actions.ErrCreatePRForProtectedBranch) {
|
|
|
+ c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusPreconditionFailed))
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if request.BuildConfig != nil {
|
|
|
- _, err = createBuildConfig(c.Config(), release, request.BuildConfig)
|
|
|
- }
|
|
|
-
|
|
|
- if err != nil {
|
|
|
- c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
c.Config().AnalyticsClient.Track(analytics.ApplicationLaunchSuccessTrack(
|
|
|
&analytics.ApplicationLaunchSuccessTrackOpts{
|
|
|
ApplicationScopedTrackOpts: analytics.GetApplicationScopedTrackOpts(
|
|
|
@@ -204,11 +214,13 @@ func (c *CreateReleaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
|
|
FlowID: operationID,
|
|
|
},
|
|
|
))
|
|
|
+
|
|
|
+ w.WriteHeader(http.StatusCreated)
|
|
|
}
|
|
|
|
|
|
-func createReleaseFromHelmRelease(
|
|
|
+func CreateAppReleaseFromHelmRelease(
|
|
|
config *config.Config,
|
|
|
- projectID, clusterID uint,
|
|
|
+ projectID, clusterID, stackResourceID uint,
|
|
|
helmRelease *release.Release,
|
|
|
) (*models.Release, error) {
|
|
|
token, err := encryption.GenerateRandomBytes(16)
|
|
|
@@ -232,12 +244,29 @@ func createReleaseFromHelmRelease(
|
|
|
}
|
|
|
|
|
|
release := &models.Release{
|
|
|
- ClusterID: clusterID,
|
|
|
- ProjectID: projectID,
|
|
|
- Namespace: helmRelease.Namespace,
|
|
|
- Name: helmRelease.Name,
|
|
|
- WebhookToken: token,
|
|
|
- ImageRepoURI: repoStr,
|
|
|
+ ClusterID: clusterID,
|
|
|
+ ProjectID: projectID,
|
|
|
+ Namespace: helmRelease.Namespace,
|
|
|
+ Name: helmRelease.Name,
|
|
|
+ WebhookToken: token,
|
|
|
+ ImageRepoURI: repoStr,
|
|
|
+ StackResourceID: stackResourceID,
|
|
|
+ }
|
|
|
+
|
|
|
+ return config.Repo.Release().CreateRelease(release)
|
|
|
+}
|
|
|
+
|
|
|
+func CreateAddonReleaseFromHelmRelease(
|
|
|
+ config *config.Config,
|
|
|
+ projectID, clusterID, stackResourceID uint,
|
|
|
+ helmRelease *release.Release,
|
|
|
+) (*models.Release, error) {
|
|
|
+ release := &models.Release{
|
|
|
+ ClusterID: clusterID,
|
|
|
+ ProjectID: projectID,
|
|
|
+ Namespace: helmRelease.Namespace,
|
|
|
+ Name: helmRelease.Name,
|
|
|
+ StackResourceID: stackResourceID,
|
|
|
}
|
|
|
|
|
|
return config.Repo.Release().CreateRelease(release)
|
|
|
@@ -245,7 +274,6 @@ func createReleaseFromHelmRelease(
|
|
|
|
|
|
func createGitAction(
|
|
|
config *config.Config,
|
|
|
- project *models.Project,
|
|
|
userID, projectID, clusterID uint,
|
|
|
request *types.CreateGitActionConfigRequest,
|
|
|
name, namespace string,
|
|
|
@@ -287,63 +315,88 @@ func createGitAction(
|
|
|
|
|
|
// if this isn't a dry run, generate the token
|
|
|
if !isDryRun {
|
|
|
- encoded, err = getToken(config, project, userID, projectID, clusterID, request)
|
|
|
+ encoded, err = getToken(config, userID, projectID, clusterID, request)
|
|
|
|
|
|
if err != nil {
|
|
|
return nil, nil, err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // create the commit in the git repo
|
|
|
- gaRunner := &actions.GithubActions{
|
|
|
- InstanceName: config.ServerConf.InstanceName,
|
|
|
- ServerURL: config.ServerConf.ServerURL,
|
|
|
- GithubOAuthIntegration: nil,
|
|
|
- GithubAppID: config.GithubAppConf.AppID,
|
|
|
- GithubAppSecretPath: config.GithubAppConf.SecretPath,
|
|
|
- GithubInstallationID: request.GitRepoID,
|
|
|
- GitRepoName: repoSplit[1],
|
|
|
- GitRepoOwner: repoSplit[0],
|
|
|
- Repo: config.Repo,
|
|
|
- ProjectID: projectID,
|
|
|
- ClusterID: clusterID,
|
|
|
- ReleaseName: name,
|
|
|
- ReleaseNamespace: namespace,
|
|
|
- GitBranch: request.GitBranch,
|
|
|
- DockerFilePath: request.DockerfilePath,
|
|
|
- FolderPath: request.FolderPath,
|
|
|
- ImageRepoURL: request.ImageRepoURI,
|
|
|
- PorterToken: encoded,
|
|
|
- Version: "v0.1.0",
|
|
|
- ShouldCreateWorkflow: request.ShouldCreateWorkflow,
|
|
|
- DryRun: isDryRun,
|
|
|
- }
|
|
|
-
|
|
|
- // Save the github err for after creating the git action config. However, we
|
|
|
- // need to call Setup() in order to get the workflow file before writing the
|
|
|
- // action config, in the case of a dry run, since the dry run does not create
|
|
|
- // a git action config.
|
|
|
- workflowYAML, githubErr := gaRunner.Setup()
|
|
|
+ var workflowYAML []byte
|
|
|
+ var gitErr error
|
|
|
+
|
|
|
+ if request.GitlabIntegrationID != 0 {
|
|
|
+ giRunner := &gitlab.GitlabCI{
|
|
|
+ ServerURL: config.ServerConf.ServerURL,
|
|
|
+ GitRepoOwner: repoSplit[0],
|
|
|
+ GitRepoName: repoSplit[1],
|
|
|
+ GitBranch: request.GitBranch,
|
|
|
+ Repo: config.Repo,
|
|
|
+ ProjectID: projectID,
|
|
|
+ ClusterID: clusterID,
|
|
|
+ UserID: userID,
|
|
|
+ IntegrationID: request.GitlabIntegrationID,
|
|
|
+ PorterConf: config,
|
|
|
+ ReleaseName: name,
|
|
|
+ ReleaseNamespace: namespace,
|
|
|
+ FolderPath: request.FolderPath,
|
|
|
+ PorterToken: encoded,
|
|
|
+ }
|
|
|
|
|
|
- if gaRunner.DryRun {
|
|
|
- if githubErr != nil {
|
|
|
- return nil, nil, githubErr
|
|
|
+ gitErr = giRunner.Setup()
|
|
|
+ } else {
|
|
|
+ // create the commit in the git repo
|
|
|
+ gaRunner := &actions.GithubActions{
|
|
|
+ InstanceName: config.ServerConf.InstanceName,
|
|
|
+ ServerURL: config.ServerConf.ServerURL,
|
|
|
+ GithubOAuthIntegration: nil,
|
|
|
+ GithubAppID: config.GithubAppConf.AppID,
|
|
|
+ GithubAppSecretPath: config.GithubAppConf.SecretPath,
|
|
|
+ GithubInstallationID: request.GitRepoID,
|
|
|
+ GitRepoName: repoSplit[1],
|
|
|
+ GitRepoOwner: repoSplit[0],
|
|
|
+ Repo: config.Repo,
|
|
|
+ ProjectID: projectID,
|
|
|
+ ClusterID: clusterID,
|
|
|
+ ReleaseName: name,
|
|
|
+ ReleaseNamespace: namespace,
|
|
|
+ GitBranch: request.GitBranch,
|
|
|
+ DockerFilePath: request.DockerfilePath,
|
|
|
+ FolderPath: request.FolderPath,
|
|
|
+ ImageRepoURL: request.ImageRepoURI,
|
|
|
+ PorterToken: encoded,
|
|
|
+ Version: "v0.1.0",
|
|
|
+ ShouldCreateWorkflow: request.ShouldCreateWorkflow,
|
|
|
+ DryRun: release == nil,
|
|
|
}
|
|
|
|
|
|
- return nil, workflowYAML, nil
|
|
|
+ // Save the github err for after creating the git action config. However, we
|
|
|
+ // need to call Setup() in order to get the workflow file before writing the
|
|
|
+ // action config, in the case of a dry run, since the dry run does not create
|
|
|
+ // a git action config.
|
|
|
+ workflowYAML, gitErr = gaRunner.Setup()
|
|
|
+
|
|
|
+ if gaRunner.DryRun {
|
|
|
+ if gitErr != nil {
|
|
|
+ return nil, nil, gitErr
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil, workflowYAML, nil
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// handle write to the database
|
|
|
ga, err := config.Repo.GitActionConfig().CreateGitActionConfig(&models.GitActionConfig{
|
|
|
- ReleaseID: release.ID,
|
|
|
- GitRepo: request.GitRepo,
|
|
|
- GitBranch: request.GitBranch,
|
|
|
- ImageRepoURI: request.ImageRepoURI,
|
|
|
- GitRepoID: request.GitRepoID,
|
|
|
- DockerfilePath: request.DockerfilePath,
|
|
|
- FolderPath: request.FolderPath,
|
|
|
- IsInstallation: true,
|
|
|
- Version: "v0.1.0",
|
|
|
+ ReleaseID: release.ID,
|
|
|
+ GitRepo: request.GitRepo,
|
|
|
+ GitBranch: request.GitBranch,
|
|
|
+ ImageRepoURI: request.ImageRepoURI,
|
|
|
+ GitRepoID: request.GitRepoID,
|
|
|
+ GitlabIntegrationID: request.GitlabIntegrationID,
|
|
|
+ DockerfilePath: request.DockerfilePath,
|
|
|
+ FolderPath: request.FolderPath,
|
|
|
+ IsInstallation: true,
|
|
|
+ Version: "v0.1.0",
|
|
|
})
|
|
|
|
|
|
if err != nil {
|
|
|
@@ -359,16 +412,11 @@ func createGitAction(
|
|
|
return nil, nil, err
|
|
|
}
|
|
|
|
|
|
- if githubErr != nil {
|
|
|
- return nil, nil, githubErr
|
|
|
- }
|
|
|
-
|
|
|
- return ga.ToGitActionConfigType(), workflowYAML, nil
|
|
|
+ return ga.ToGitActionConfigType(), workflowYAML, gitErr
|
|
|
}
|
|
|
|
|
|
func getToken(
|
|
|
config *config.Config,
|
|
|
- proj *models.Project,
|
|
|
userID, projectID, clusterID uint,
|
|
|
request *types.CreateGitActionConfigRequest,
|
|
|
) (string, error) {
|
|
|
@@ -454,10 +502,6 @@ func getToken(
|
|
|
SecretKey: hashedToken,
|
|
|
}
|
|
|
|
|
|
- if !proj.APITokensEnabled {
|
|
|
- return "", fmt.Errorf("api tokens are not enabled for this project")
|
|
|
- }
|
|
|
-
|
|
|
apiToken, err = config.Repo.APIToken().CreateAPIToken(apiToken)
|
|
|
|
|
|
if err != nil {
|
|
|
@@ -512,7 +556,7 @@ type containerEnvConfig struct {
|
|
|
} `yaml:"container"`
|
|
|
}
|
|
|
|
|
|
-func getGARunner(
|
|
|
+func GetGARunner(
|
|
|
config *config.Config,
|
|
|
userID, projectID, clusterID uint,
|
|
|
ga *models.GitActionConfig,
|