Просмотр исходного кода

Merge branch 'master' into porter-yaml-upgrades

Mohammed Nafees 3 лет назад
Родитель
Сommit
151af15e8a
35 измененных файлов с 510 добавлено и 595 удалено
  1. 10 2
      .github/workflows/build-dev-cli.yaml
  2. 16 7
      .github/workflows/prerelease.yaml
  3. 8 0
      .github/workflows/release.yaml
  4. 5 4
      Makefile
  5. 1 1
      api/client/registry.go
  6. 14 0
      api/server/authn/handler.go
  7. 1 1
      api/server/handlers/environment/finalize_deployment_with_errors.go
  8. 1 1
      api/server/handlers/helmrepo/create.go
  9. 112 0
      api/server/handlers/helmrepo/update.go
  10. 11 2
      api/server/handlers/infra/forms.go
  11. 29 0
      api/server/router/helm_repo.go
  12. 2 2
      api/types/helm_repo.go
  13. 1 1
      cli/cmd/connect/helmrepo.go
  14. 3 1
      cli/cmd/errors.go
  15. 52 0
      cli/cmd/errors/error_handler.go
  16. 6 274
      cli/cmd/portforward.go
  17. 27 0
      cli/main.go
  18. 0 15
      dashboard/package-lock.json
  19. 0 3
      dashboard/package.json
  20. 1 1
      dashboard/src/components/porter-form/FormDebugger.tsx
  21. 6 4
      dashboard/src/components/porter-form/field-components/KeyValueArray.tsx
  22. 3 1
      dashboard/src/components/repo-selector/ActionDetails.tsx
  23. 0 5
      dashboard/src/index.tsx
  24. 0 8
      dashboard/src/main/Main.tsx
  25. 0 10
      dashboard/src/main/home/Home.tsx
  26. 10 1
      dashboard/src/main/home/cluster-dashboard/expanded-chart/jobs/ConnectToJobInstructionsModal.tsx
  27. 0 12
      dashboard/src/main/home/onboarding/steps/ProvisionResources/ProvisionResources.tsx
  28. 10 32
      dashboard/src/main/home/provisioner/AWSFormSection.tsx
  29. 8 25
      dashboard/src/shared/error_handling/logger.ts
  30. 1 24
      dashboard/src/shared/error_handling/sentry/setup.ts
  31. 161 149
      docs/getting-started/aws.md
  32. 5 0
      internal/auth/token/token.go
  33. 2 2
      internal/repository/gorm/session.go
  34. 3 6
      services/cli_install_script_container/install.sh
  35. 1 1
      services/porter_cli_container/dev.Dockerfile

+ 10 - 2
.github/workflows/build-dev-cli.yaml

@@ -20,11 +20,19 @@ jobs:
         id: login-ecr
         run: |
           aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/o1j4x7p4
+      - name: Login to GHCR
+        id: login-ghcr
+        run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
       - name: Build
         run: |
           DOCKER_BUILDKIT=1 docker build . \
             -t public.ecr.aws/o1j4x7p4/porter-cli:dev \
-            -f ./services/porter_cli_container/dev.Dockerfile
-      - name: Push
+            -f ./services/porter_cli_container/dev.Dockerfile \
+            --build-arg SENTRY_DSN=${{ secrets.SENTRY_DSN }}
+      - name: Push to ECR public
         run: |
           docker push public.ecr.aws/o1j4x7p4/porter-cli:dev
+      - name: Push to GHCR
+        run: |
+          docker tag public.ecr.aws/o1j4x7p4/porter-cli:dev ghcr.io/porter-dev/porter/porter-cli:dev
+          docker push ghcr.io/porter-dev/porter/porter-cli:dev

+ 16 - 7
.github/workflows/prerelease.yaml

@@ -142,7 +142,7 @@ jobs:
           NODE_ENV: production
       - name: Build Linux binaries
         run: |
-          go build -ldflags="-w -s -X 'github.com/porter-dev/porter/cli/cmd/config.Version=${{steps.tag_name.outputs.tag}}'" -a -tags cli -o ./porter ./cli &
+          go build -ldflags="-w -s -X 'github.com/porter-dev/porter/cli/cmd/config.Version=${{steps.tag_name.outputs.tag}}' -X 'github.com/porter-dev/porter/cli/cmd/errors.SentryDSN=${{secrets.SENTRY_DSN}}'" -a -tags cli -o ./porter ./cli &
           go build -ldflags="-w -s -X 'main.Version=${{steps.tag_name.outputs.tag}}'" -a -o ./docker-credential-porter ./cmd/docker-credential-porter/ &
           go build -ldflags="-w -s -X 'main.Version=${{steps.tag_name.outputs.tag}}'" -a -tags ee -o ./portersvr ./cmd/app/ &
           wait
@@ -199,7 +199,7 @@ jobs:
           EOL
       - name: Build and Zip MacOS amd64 binaries
         run: |
-          go build -ldflags="-w -s -X 'github.com/porter-dev/porter/cli/cmd/config.Version=${{steps.tag_name.outputs.tag}}'" -a -tags cli -o ./amd64/porter ./cli &
+          go build -ldflags="-w -s -X 'github.com/porter-dev/porter/cli/cmd/config.Version=${{steps.tag_name.outputs.tag}}' -X 'github.com/porter-dev/porter/cli/cmd/errors.SentryDSN=${{secrets.SENTRY_DSN}}'" -a -tags cli -o ./amd64/porter ./cli &
           go build -ldflags="-w -s -X 'main.Version=${{steps.tag_name.outputs.tag}}'" -a -o ./amd64/docker-credential-porter ./cmd/docker-credential-porter/ &
           go build -ldflags="-w -s -X 'main.Version=${{steps.tag_name.outputs.tag}}'" -a -tags ee -o ./amd64/portersvr ./cmd/app/ &
           wait
@@ -457,15 +457,22 @@ jobs:
         id: login-ecr
         run: |
           aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/o1j4x7p4
+      - name: Login to GHCR
+        id: login-ghcr
+        run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
       - name: Build
         run: |
           docker build ./services/porter_cli_container \
             -t public.ecr.aws/o1j4x7p4/porter-cli:${{steps.tag_name.outputs.tag}} \
             -f ./services/porter_cli_container/Dockerfile \
-            --build-arg VERSION=${{steps.tag_name.outputs.tag}}
-      - name: Push
+            --build-arg VERSION=${{steps.tag_name.outputs.tag}} \
+            --build-arg SENTRY_DSN=${{secrets.SENTRY_DSN}}
+      - name: Push to ECR public
+        run: docker push public.ecr.aws/o1j4x7p4/porter-cli:${{steps.tag_name.outputs.tag}}
+      - name: Push to GHCR
         run: |
-          docker push public.ecr.aws/o1j4x7p4/porter-cli:${{steps.tag_name.outputs.tag}}
+          docker tag public.ecr.aws/o1j4x7p4/porter-cli:${{steps.tag_name.outputs.tag}} ghcr.io/porter-dev/porter/porter-cli:${{steps.tag_name.outputs.tag}}
+          docker push ghcr.io/porter-dev/porter/porter-cli:${{steps.tag_name.outputs.tag}}
   update-porter-update-action:
     name: Update porter-update-action
     runs-on: ubuntu-latest
@@ -489,7 +496,8 @@ jobs:
           git checkout -B "${{steps.tag_name.outputs.tag}}"
 
           cat >Dockerfile <<EOL
-          FROM public.ecr.aws/o1j4x7p4/porter-cli:${{steps.tag_name.outputs.tag}}
+          FROM ghcr.io/porter-dev/porter/porter-cli:${{steps.tag_name.outputs.tag}}
+          LABEL org.opencontainers.image.source="https://github.com/porter-dev/porter"
 
           COPY entrypoint.sh /action/
 
@@ -527,7 +535,8 @@ jobs:
           git checkout -B "${{steps.tag_name.outputs.tag}}"
 
           cat >Dockerfile <<EOL
-          FROM public.ecr.aws/o1j4x7p4/porter-cli:${{steps.tag_name.outputs.tag}}
+          FROM ghcr.io/porter-dev/porter/porter-cli:${{steps.tag_name.outputs.tag}}
+          LABEL org.opencontainers.image.source="https://github.com/porter-dev/porter"
 
           COPY entrypoint.sh /action/
 

+ 8 - 0
.github/workflows/release.yaml

@@ -58,11 +58,18 @@ jobs:
         id: login-ecr
         run: |
           aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/o1j4x7p4
+      - name: Login to GHCR
+        id: login-ghcr
+        run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
       - name: Pull versioned CLI image and push to latest
         run: |
           docker pull public.ecr.aws/o1j4x7p4/porter-cli:${{steps.tag_name.outputs.tag}}
           docker tag public.ecr.aws/o1j4x7p4/porter-cli:${{steps.tag_name.outputs.tag}} public.ecr.aws/o1j4x7p4/porter-cli:latest
           docker push public.ecr.aws/o1j4x7p4/porter-cli:latest
+
+          docker pull ghcr.io/porter-dev/porter/porter-cli:${{steps.tag_name.outputs.tag}}
+          docker tag ghcr.io/porter-dev/porter/porter-cli:${{steps.tag_name.outputs.tag}} ghcr.io/porter-dev/porter/porter-cli:latest
+          docker push ghcr.io/porter-dev/porter/porter-cli:latest
   update-homebrew-repo:
     name: Update the Homebrew repo with the new CLI version
     runs-on: ubuntu-latest
@@ -81,6 +88,7 @@ jobs:
           curl -L https://github.com/porter-dev/porter/releases/download/${version}/porter_${version}_Darwin_x86_64.zip --output $name
 
           sha=$(cat porter_${{steps.tag_name.outputs.tag}}_Darwin_x86_64.zip | openssl sha256 | sed 's/(stdin)= //g')
+          sha=${sha#"SHA256"}
 
           cat >porter.rb <<EOL
           class Porter < Formula

+ 5 - 4
Makefile

@@ -1,5 +1,6 @@
-BINDIR      := $(CURDIR)/bin
-VERSION ?= dev
+BINDIR     := $(CURDIR)/bin
+VERSION    ?= dev
+SENTRY_DSN ?= 
 
 start-dev: install setup-env-files
 	bash ./scripts/dev-environment/StartDevServer.sh
@@ -14,10 +15,10 @@ setup-env-files:
 	bash ./scripts/dev-environment/CreateDefaultEnvFiles.sh
 
 build-cli:
-	go build -ldflags="-w -s -X 'github.com/porter-dev/porter/cli/cmd/config.Version=${VERSION}'" -a -tags cli -o $(BINDIR)/porter ./cli
+	go build -ldflags="-w -s -X 'github.com/porter-dev/porter/cli/cmd/config.Version=${VERSION}' -X 'github.com/porter-dev/porter/cli/cmd/errors.SentryDSN=${SENTRY_DSN}'" -a -tags cli -o $(BINDIR)/porter ./cli
 
 build-cli-dev:
-	go build -ldflags="-X 'github.com/porter-dev/porter/cli/cmd/config.Version=${VERSION}'" -tags cli -o $(BINDIR)/porter ./cli
+	go build -ldflags="-X 'github.com/porter-dev/porter/cli/cmd/config.Version=${VERSION}' -X 'github.com/porter-dev/porter/cli/cmd/errors.SentryDSN=${SENTRY_DSN}'" -tags cli -o $(BINDIR)/porter ./cli
 
 start-provisioner-dev: install setup-env-files
 	bash ./scripts/dev-environment/StartProvisionerServer.sh

+ 1 - 1
api/client/registry.go

@@ -31,7 +31,7 @@ func (c *Client) CreateRegistry(
 func (c *Client) CreateHelmRepo(
 	ctx context.Context,
 	projectID uint,
-	req *types.CreateHelmRepoRequest,
+	req *types.CreateUpdateHelmRepoRequest,
 ) (*types.HelmRepo, error) {
 	resp := &types.HelmRepo{}
 

+ 14 - 0
api/server/authn/handler.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"net/http"
 	"strings"
+	"time"
 
 	"github.com/gorilla/sessions"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
@@ -81,6 +82,19 @@ func (authn *AuthN) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	supportEmail := "support@porter.run"
+	cancelTime := time.Date(2023, 01, 31, 14, 30, 0, 0, time.Now().Local().Location())
+	if email, ok := session.Values["email"]; ok {
+		if email.(string) == supportEmail {
+			sess, _ := authn.config.Repo.Session().SelectSession(&models.Session{Key: session.ID})
+			if sess.CreatedAt.Before(cancelTime) {
+				_, _ = authn.config.Repo.Session().DeleteSession(sess)
+				authn.handleForbiddenForSession(w, r, fmt.Errorf("error, contact admin"), session)
+				return
+			}
+		}
+	}
+
 	if auth, ok := session.Values["authenticated"].(bool); !auth || !ok {
 		authn.handleForbiddenForSession(w, r, fmt.Errorf("stored cookie was not authenticated"), session)
 		return

+ 1 - 1
api/server/handlers/environment/finalize_deployment_with_errors.go

@@ -125,7 +125,7 @@ func (c *FinalizeDeploymentWithErrorsHandler) ServeHTTP(w http.ResponseWriter, r
 	var lastErrors []string
 
 	for resName, errString := range request.Errors {
-		lastErrors = append(lastErrors, "%s: %s,", resName, errString)
+		lastErrors = append(lastErrors, fmt.Sprintf("%s: %s", resName, errString))
 	}
 
 	depl.LastErrors = strings.Join(lastErrors, ",")

+ 1 - 1
api/server/handlers/helmrepo/create.go

@@ -31,7 +31,7 @@ func NewHelmRepoCreateHandler(
 func (p *HelmRepoCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
 
-	request := &types.CreateHelmRepoRequest{}
+	request := &types.CreateUpdateHelmRepoRequest{}
 
 	ok := p.DecodeAndValidate(w, r, request)
 

+ 112 - 0
api/server/handlers/helmrepo/update.go

@@ -0,0 +1,112 @@
+package helmrepo
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/requestutils"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models"
+	"gorm.io/gorm"
+)
+
+type HelmRepoUpdateHandler struct {
+	handlers.PorterHandlerReadWriter
+}
+
+func NewHelmRepoUpdateHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *HelmRepoUpdateHandler {
+	return &HelmRepoUpdateHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+	}
+}
+
+func (p *HelmRepoUpdateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
+
+	helmRepoID, reqErr := requestutils.GetURLParamUint(r, "helm_repo_id")
+
+	if reqErr != nil {
+		p.HandleAPIError(w, r, reqErr)
+		return
+	}
+
+	helmRepo, err := p.Repo().HelmRepo().ReadHelmRepo(proj.ID, helmRepoID)
+
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			p.HandleAPIError(w, r, apierrors.NewErrNotFound(fmt.Errorf("no such helm repo")))
+			return
+		}
+
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	request := &types.CreateUpdateHelmRepoRequest{}
+
+	ok := p.DecodeAndValidate(w, r, request)
+
+	if !ok {
+		return
+	}
+
+	if request.BasicIntegrationID != 0 &&
+		helmRepo.BasicAuthIntegrationID != 0 &&
+		request.BasicIntegrationID != helmRepo.BasicAuthIntegrationID {
+		bi, err := p.Repo().BasicIntegration().ReadBasicIntegration(proj.ID, helmRepo.BasicAuthIntegrationID)
+
+		if err != nil {
+			if !errors.Is(err, gorm.ErrRecordNotFound) {
+				p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+				return
+			}
+		} else {
+			_, err = p.Repo().BasicIntegration().DeleteBasicIntegration(bi)
+
+			if err != nil {
+				p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+				return
+			}
+		}
+	}
+
+	// if a basic integration is specified, verify that it exists in the project
+	if request.BasicIntegrationID != 0 {
+		_, err := p.Repo().BasicIntegration().ReadBasicIntegration(proj.ID, request.BasicIntegrationID)
+
+		if err != nil {
+			if errors.Is(err, gorm.ErrRecordNotFound) {
+				p.HandleAPIError(w, r, apierrors.NewErrForbidden(
+					fmt.Errorf("basic integration with id %d not found in project %d", request.BasicIntegrationID, proj.ID),
+				))
+
+				return
+			}
+
+			p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+			return
+		}
+	}
+
+	helmRepo.Name = request.Name
+	helmRepo.RepoURL = request.URL
+	helmRepo.BasicAuthIntegrationID = request.BasicIntegrationID
+
+	helmRepo, err = p.Repo().HelmRepo().UpdateHelmRepo(helmRepo)
+
+	if err != nil {
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	p.WriteResult(w, r, helmRepo.ToHelmRepoType())
+}

+ 11 - 2
api/server/handlers/infra/forms.go

@@ -297,7 +297,7 @@ tabs:
           value: "12.7"
         - label: "v12.8"
           value: "12.8"
-          - label: "v12.9"
+        - label: "v12.9"
           value: "12.9"
         - label: "v12.10"
           value: "12.10"
@@ -471,7 +471,7 @@ tabs:
           value: m6i.4xlarge
         - label: r5.large
           value: r5.large
-        - value: r5.xlarge
+        - label: r5.xlarge
           value: r5.xlarge
     - type: string-input
       label: 👤 Issuer Email
@@ -758,6 +758,15 @@ tabs:
           value: t3.large
         - label: t3.xlarge
           value: t3.xlarge
+  - name: kms_secret_encryption
+    contents:
+    - type: heading
+      label: KMS Encryption
+    - type: checkbox
+      variable: is_kms_enabled
+      label: Encrypt all Kubernetes secrets with AWS Key Management Service (KMS)
+      settings:
+        default: false
 `
 
 const gcrForm = `name: GCR

+ 29 - 0
api/server/router/helm_repo.go

@@ -81,6 +81,35 @@ func getHelmRepoRoutes(
 		Router:   r,
 	})
 
+	// PATCH /api/projects/{project_id}/helmrepos/{helm_repo_id} -> registry.NewHelmRepoUpdateHandler
+	updateEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbUpdate,
+			Method: types.HTTPVerbPatch,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath,
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.HelmRepoScope,
+			},
+		},
+	)
+
+	updateHandler := helmrepo.NewHelmRepoUpdateHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: updateEndpoint,
+		Handler:  updateHandler,
+		Router:   r,
+	})
+
 	// DELETE /api/projects/{project_id}/helmrepos/{helm_repo_id} -> registry.NewHelmRepoDeleteHandler
 	deleteEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

+ 2 - 2
api/types/helm_repo.go

@@ -14,8 +14,8 @@ type HelmRepo struct {
 
 type GetHelmRepoResponse HelmRepo
 
-type CreateHelmRepoRequest struct {
-	URL                string `json:"url"`
+type CreateUpdateHelmRepoRequest struct {
+	URL                string `json:"url" form:"required"`
 	Name               string `json:"name" form:"required"`
 	BasicIntegrationID uint   `json:"basic_integration_id"`
 }

+ 1 - 1
cli/cmd/connect/helmrepo.go

@@ -77,7 +77,7 @@ Password:`)
 	reg, err := client.CreateHelmRepo(
 		context.Background(),
 		projectID,
-		&types.CreateHelmRepoRequest{
+		&types.CreateUpdateHelmRepoRequest{
 			URL:                repoURL,
 			Name:               repoName,
 			BasicIntegrationID: basicIntegrationID,

+ 3 - 1
cli/cmd/errors.go

@@ -10,6 +10,7 @@ import (
 	api "github.com/porter-dev/porter/api/client"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/cli/cmd/config"
+	cliErrors "github.com/porter-dev/porter/cli/cmd/errors"
 )
 
 var ErrNotLoggedIn error = errors.New("You are not logged in.")
@@ -52,7 +53,8 @@ func checkLoginAndRun(args []string, runner func(user *types.GetAuthenticatedUse
 			return nil
 		}
 
-		red.Fprintf(os.Stderr, "Error: %v\n", err.Error())
+		cliErrors.GetErrorHandler().HandleError(err)
+
 		return err
 	}
 

+ 52 - 0
cli/cmd/errors/error_handler.go

@@ -0,0 +1,52 @@
+package errors
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/fatih/color"
+	"github.com/getsentry/sentry-go"
+	"github.com/porter-dev/porter/cli/cmd/config"
+)
+
+var SentryDSN string = ""
+
+type errorHandler interface {
+	HandleError(error)
+}
+
+type standardErrorHandler struct{}
+
+func (h *standardErrorHandler) HandleError(err error) {
+	color.New(color.FgRed).Fprintf(os.Stderr, "error: %s\n", err.Error())
+}
+
+type sentryErrorHandler struct{}
+
+func (h *sentryErrorHandler) HandleError(err error) {
+	if SentryDSN != "" {
+		localHub := sentry.CurrentHub().Clone()
+
+		localHub.ConfigureScope(func(scope *sentry.Scope) {
+			scope.SetTags(map[string]string{
+				"host":    config.GetCLIConfig().Host,
+				"project": fmt.Sprintf("%d", config.GetCLIConfig().Project),
+				"cluster": fmt.Sprintf("%d", config.GetCLIConfig().Cluster),
+			})
+		})
+
+		if eventID := localHub.CaptureException(err); eventID == nil {
+			color.New(color.FgRed).Fprintf(os.Stderr, "error in sending exception to sentry\n")
+		}
+	}
+
+	color.New(color.FgRed).Fprintf(os.Stderr, "error: %s\n", err.Error())
+}
+
+func GetErrorHandler() errorHandler {
+	if SentryDSN != "" {
+		return &sentryErrorHandler{}
+	}
+
+	return &standardErrorHandler{}
+}

+ 6 - 274
cli/cmd/portforward.go

@@ -1,288 +1,20 @@
 package cmd
 
 import (
-	"context"
 	"fmt"
-	"net/http"
-	"net/url"
-	"os"
-	"os/signal"
-	"strconv"
-	"strings"
-	"time"
 
-	"github.com/briandowns/spinner"
-	api "github.com/porter-dev/porter/api/client"
-	"github.com/porter-dev/porter/api/types"
-	"github.com/porter-dev/porter/cli/cmd/utils"
+	"github.com/fatih/color"
 	"github.com/spf13/cobra"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/apimachinery/pkg/runtime/schema"
-	"k8s.io/apimachinery/pkg/util/sets"
-	"k8s.io/client-go/rest"
-	"k8s.io/client-go/tools/clientcmd"
-	"k8s.io/client-go/tools/portforward"
-	"k8s.io/client-go/transport/spdy"
-	"k8s.io/kubectl/pkg/util"
 )
 
-var address []string
-
 var portForwardCmd = &cobra.Command{
-	Use:   "port-forward [release] [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N]",
-	Short: "Forward one or more local ports to a pod of a release",
-	Args:  cobra.MinimumNArgs(2),
-	Run: func(cmd *cobra.Command, args []string) {
-		err := checkLoginAndRun(args, portForward)
-
-		if err != nil {
-			os.Exit(1)
-		}
-	},
+	Use: "port-forward [release] [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N]",
+	Deprecated: fmt.Sprintf("please use the %s command instead.",
+		color.New(color.FgYellow, color.Bold).Sprintf("porter kubectl -- port-forward"),
+	),
+	DisableFlagParsing: true,
 }
 
 func init() {
-	portForwardCmd.PersistentFlags().StringVar(
-		&namespace,
-		"namespace",
-		"default",
-		"namespace of the release whose pod you want to port-forward to",
-	)
-
-	portForwardCmd.Flags().StringSliceVar(
-		&address,
-		"address",
-		[]string{"localhost"},
-		"Addresses to listen on (comma separated). Only accepts IP addresses or localhost as a value. "+
-			"When localhost is supplied, kubectl will try  to bind on both 127.0.0.1 and ::1 and will fail "+
-			"if neither of these addresses are available to bind.")
-
 	rootCmd.AddCommand(portForwardCmd)
 }
-
-func forwardPorts(
-	method string,
-	url *url.URL,
-	kubeConfig *rest.Config,
-	address, ports []string,
-	stopChan <-chan struct{},
-	readyChan chan struct{},
-) error {
-	transport, upgrader, err := spdy.RoundTripperFor(kubeConfig)
-
-	if err != nil {
-		return err
-	}
-
-	dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, method, url)
-	fw, err := portforward.NewOnAddresses(
-		dialer, address, ports, stopChan, readyChan, os.Stdout, os.Stderr)
-
-	if err != nil {
-		return err
-	}
-
-	return fw.ForwardPorts()
-}
-
-// splitPort splits port string which is in form of [LOCAL PORT]:REMOTE PORT
-// and returns local and remote ports separately
-func splitPort(port string) (local, remote string) {
-	parts := strings.Split(port, ":")
-	if len(parts) == 2 {
-		return parts[0], parts[1]
-	}
-
-	return parts[0], parts[0]
-}
-
-func portForward(user *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
-	var err error
-	var pod corev1.Pod
-
-	s := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
-	s.Color("cyan")
-	s.Suffix = fmt.Sprintf(" Loading list of pods for %s", args[0])
-	s.Start()
-
-	podsResp, err := client.GetK8sAllPods(context.Background(), cliConf.Project, cliConf.Cluster, namespace, args[0])
-
-	s.Stop()
-
-	if err != nil {
-		return err
-	}
-
-	pods := *podsResp
-
-	if len(pods) > 1 {
-		selectedPod, err := utils.PromptSelect("Select a pod to port-forward", func() []string {
-			var names []string
-
-			for i, pod := range pods {
-				names = append(names, fmt.Sprintf("%d - %s", (i+1), pod.Name))
-			}
-
-			return names
-		}())
-
-		if err != nil {
-			return err
-		}
-
-		podIdxStr := strings.Split(selectedPod, " - ")[0]
-
-		podIdx, err := strconv.Atoi(podIdxStr)
-
-		if err != nil {
-			return err
-		}
-
-		pod = pods[podIdx-1]
-	} else {
-		pod = pods[0]
-	}
-
-	kubeResp, err := client.GetKubeconfig(context.Background(), cliConf.Project, cliConf.Cluster, cliConf.Kubeconfig)
-
-	if err != nil {
-		return err
-	}
-
-	kubeBytes := kubeResp.Kubeconfig
-
-	cmdConf, err := clientcmd.NewClientConfigFromBytes(kubeBytes)
-
-	if err != nil {
-		return err
-	}
-
-	restConf, err := cmdConf.ClientConfig()
-
-	if err != nil {
-		return err
-	}
-
-	restConf.GroupVersion = &schema.GroupVersion{
-		Group:   "api",
-		Version: "v1",
-	}
-
-	restConf.NegotiatedSerializer = runtime.NewSimpleNegotiatedSerializer(runtime.SerializerInfo{})
-
-	restClient, err := rest.RESTClientFor(restConf)
-
-	if err != nil {
-		return err
-	}
-
-	err = checkUDPPortInPod(args[1:], &pod)
-
-	if err != nil {
-		return err
-	}
-
-	ports, err := convertPodNamedPortToNumber(args[1:], pod)
-
-	if err != nil {
-		return err
-	}
-
-	stopChannel := make(chan struct{}, 1)
-	readyChannel := make(chan struct{})
-
-	signals := make(chan os.Signal, 1)
-	signal.Notify(signals, os.Interrupt)
-	defer signal.Stop(signals)
-
-	go func() {
-		<-signals
-		if stopChannel != nil {
-			close(stopChannel)
-		}
-	}()
-
-	req := restClient.Post().
-		Resource("pods").
-		Namespace(namespace).
-		Name(pod.Name).
-		SubResource("portforward")
-
-	return forwardPorts("POST", req.URL(), restConf, address, ports, stopChannel, readyChannel)
-}
-
-func checkUDPPortInPod(ports []string, pod *corev1.Pod) error {
-	udpPorts := sets.NewInt()
-	tcpPorts := sets.NewInt()
-	for _, ct := range pod.Spec.Containers {
-		for _, ctPort := range ct.Ports {
-			portNum := int(ctPort.ContainerPort)
-			switch ctPort.Protocol {
-			case corev1.ProtocolUDP:
-				udpPorts.Insert(portNum)
-			case corev1.ProtocolTCP:
-				tcpPorts.Insert(portNum)
-			}
-		}
-	}
-	return checkUDPPorts(udpPorts.Difference(tcpPorts), ports, pod)
-}
-
-func checkUDPPorts(udpOnlyPorts sets.Int, ports []string, obj metav1.Object) error {
-	for _, port := range ports {
-		_, remotePort := splitPort(port)
-		portNum, err := strconv.Atoi(remotePort)
-		if err != nil {
-			switch v := obj.(type) {
-			case *corev1.Service:
-				svcPort, err := util.LookupServicePortNumberByName(*v, remotePort)
-				if err != nil {
-					return err
-				}
-				portNum = int(svcPort)
-
-			case *corev1.Pod:
-				ctPort, err := util.LookupContainerPortNumberByName(*v, remotePort)
-				if err != nil {
-					return err
-				}
-				portNum = int(ctPort)
-
-			default:
-				return fmt.Errorf("unknown object: %v", obj)
-			}
-		}
-		if udpOnlyPorts.Has(portNum) {
-			return fmt.Errorf("UDP protocol is not supported for %s", remotePort)
-		}
-	}
-	return nil
-}
-
-func convertPodNamedPortToNumber(ports []string, pod corev1.Pod) ([]string, error) {
-	var converted []string
-	for _, port := range ports {
-		localPort, remotePort := splitPort(port)
-
-		containerPortStr := remotePort
-		_, err := strconv.Atoi(remotePort)
-		if err != nil {
-			containerPort, err := util.LookupContainerPortNumberByName(pod, remotePort)
-			if err != nil {
-				return nil, err
-			}
-
-			containerPortStr = strconv.Itoa(int(containerPort))
-		}
-
-		if localPort != remotePort {
-			converted = append(converted, fmt.Sprintf("%s:%s", localPort, containerPortStr))
-		} else {
-			converted = append(converted, containerPortStr)
-		}
-	}
-
-	return converted, nil
-}

+ 27 - 0
cli/main.go

@@ -1,11 +1,38 @@
+//go:build cli
 // +build cli
 
 package main
 
 import (
+	"os"
+	"time"
+
+	"github.com/fatih/color"
+	"github.com/getsentry/sentry-go"
 	"github.com/porter-dev/porter/cli/cmd"
+	"github.com/porter-dev/porter/cli/cmd/config"
+	"github.com/porter-dev/porter/cli/cmd/errors"
 )
 
 func main() {
+	if errors.SentryDSN != "" {
+		err := sentry.Init(sentry.ClientOptions{
+			Dsn:         errors.SentryDSN,
+			Environment: "cli",
+			Debug:       config.Version == "dev",
+			Release:     config.Version,
+			IgnoreErrors: []string{
+				"Forbidden",
+			},
+		})
+
+		if err != nil {
+			color.New(color.FgRed).Fprintf(os.Stderr, "error initialising sentry: %s\n", err)
+			os.Exit(1)
+		}
+
+		defer sentry.Flush(2 * time.Second)
+	}
+
 	cmd.Execute()
 }

+ 0 - 15
dashboard/package-lock.json

@@ -3737,16 +3737,6 @@
       "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
       "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
     },
-    "cohere-js": {
-      "version": "1.0.19",
-      "resolved": "https://registry.npmjs.org/cohere-js/-/cohere-js-1.0.19.tgz",
-      "integrity": "sha512-2XVX2LUKHjbJ4GCsnizXnAVHZfq9RM1RmHl8zE4G2ORdXmDpzSx5i0UIj/0GZ3AwjKIlYsrGA4kdCGT+WapjPQ=="
-    },
-    "cohere-sentry": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/cohere-sentry/-/cohere-sentry-1.0.1.tgz",
-      "integrity": "sha512-OHdKcc8LED8X/JQKlMD0Zapb4rcOkPu0m11+okHouMDep1/MvyOG4JXcK4Mo3sabJT65yozc9Uo+nJfSWzaFcg=="
-    },
     "collection-visit": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@@ -5789,11 +5779,6 @@
       "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
       "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
     },
-    "highlight.run": {
-      "version": "1.7.5",
-      "resolved": "https://registry.npmjs.org/highlight.run/-/highlight.run-1.7.5.tgz",
-      "integrity": "sha512-Kens7xbGJE/vZ21z+wVcdbdzn6bsFJD+gMh942cG/1ewgdWp5JmGqIiO33aJWyzEcXj0dBE8gQUi6Ql+9jSNUQ=="
-    },
     "history": {
       "version": "4.10.1",
       "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",

+ 0 - 3
dashboard/package.json

@@ -27,8 +27,6 @@
     "brace": "^0.11.1",
     "chroma-js": "^2.4.2",
     "clipboard": "^2.0.8",
-    "cohere-js": "^1.0.19",
-    "cohere-sentry": "^1.0.1",
     "color": "^4.2.3",
     "core-js": "^3.16.1",
     "cron-parser": "^4.3.0",
@@ -39,7 +37,6 @@
     "dayjs": "^1.11.5",
     "dotenv": "^8.2.0",
     "fuse.js": "^6.6.2",
-    "highlight.run": "^1.4.5",
     "ini": ">=1.3.6",
     "js-base64": "^3.6.0",
     "js-yaml": "^4.1.0",

+ 1 - 1
dashboard/src/components/porter-form/FormDebugger.tsx

@@ -61,7 +61,7 @@ export default class FormDebugger extends Component<PropsType, StateType> {
     try {
       formData = yaml.load(this.state.rawYaml);
     } catch (err: any) {
-      console.log("YAML parsing error.");
+      console.log("YAML parsing error.", err);
     }
     return (
       <StyledFormDebugger>

+ 6 - 4
dashboard/src/components/porter-form/field-components/KeyValueArray.tsx

@@ -32,14 +32,17 @@ const KeyValueArray: React.FC<Props> = (props) => {
     props.id,
     {
       initState: () => {
-        let values = props.value[0];
+        let values = {}
+        if (props?.value?.length > 0) {
+          values = props.value[0]
+        }
         const normalValues = Object.entries(values?.normal || {});
         values = omit(values, ["normal", "synced", "build"]);
         return {
           values: hasSetValue(props)
             ? ([...Object.entries(values), ...normalValues]?.map(([k, v]) => {
-                return { key: k, value: v };
-              }) as any[])
+              return { key: k, value: v };
+            }) as any[])
             : [],
           showEnvModal: false,
           showEditorModal: false,
@@ -112,7 +115,6 @@ const KeyValueArray: React.FC<Props> = (props) => {
     }
   }, [
     props.injectedProps,
-    props.value[0],
     variables?.clusterId,
     variables?.namespace,
     currentProject?.id,

+ 3 - 1
dashboard/src/components/repo-selector/ActionDetails.tsx

@@ -137,10 +137,12 @@ const ActionDetails: React.FC<PropsType> = (props) => {
         />
       )}
       <InputRow
-        disabled={true}
+        // Currently there is a bug which is failing to detect the correct application folder root path. As a hotfix, we are enabling the user to manually set the application folder path.
+        disabled={false}
         label={dockerfilePath ? "Docker build context" : "Application folder"}
         type="text"
         width="100%"
+        setValue={(value) => typeof value === "string" && setFolderPath(value)}
         value={folderPath}
       />
       {renderRegistrySection()}

+ 0 - 5
dashboard/src/index.tsx

@@ -3,7 +3,6 @@ import "regenerator-runtime/runtime";
 
 import * as React from "react";
 import * as ReactDOM from "react-dom";
-import Cohere from "cohere-js";
 import App from "./App";
 import { SetupSentry } from "shared/error_handling/sentry/setup";
 import { EnableErrorHandling } from "shared/error_handling/window_error_handling";
@@ -14,10 +13,6 @@ declare global {
   }
 }
 
-if (process.env.ENABLE_COHERE && process.env.COHERE_API_KEY) {
-  Cohere.init(process.env.COHERE_API_KEY);
-}
-
 if (process.env.ENABLE_SENTRY) {
   SetupSentry();
 }

+ 0 - 8
dashboard/src/main/Main.tsx

@@ -3,7 +3,6 @@ import { Route, Redirect, Switch } from "react-router-dom";
 
 import api from "shared/api";
 import { Context } from "shared/Context";
-import Cohere from "cohere-js";
 import ResetPasswordInit from "./auth/ResetPasswordInit";
 import ResetPasswordFinalize from "./auth/ResetPasswordFinalize";
 import Login from "./auth/Login";
@@ -42,13 +41,6 @@ export default class Main extends Component<PropsType, StateType> {
       .checkAuth("", {}, {})
       .then((res) => {
         if (res && res?.data) {
-          if (process.env.ENABLE_COHERE) {
-            Cohere.identify(res?.data?.id, {
-              displayName: res?.data?.email,
-              email: res?.data?.email,
-            });
-          }
-
           setUser(res?.data?.id, res?.data?.email);
           this.setState({
             isLoggedIn: true,

+ 0 - 10
dashboard/src/main/home/Home.tsx

@@ -3,7 +3,6 @@ import { Route, RouteComponentProps, Switch, withRouter } from "react-router";
 import styled from "styled-components";
 
 import api from "shared/api";
-import { H } from "highlight.run";
 import { Context } from "shared/Context";
 import { PorterUrl, pushFiltered, pushQueryParams } from "shared/routing";
 import { ClusterType, ProjectType } from "shared/types";
@@ -159,15 +158,6 @@ class Home extends Component<PropsType, StateType> {
 
     let { user } = this.context;
 
-    // Initialize Highlight
-    if (
-      window.location.href.includes("dashboard.getporter.dev") &&
-      !user.email.includes("@getporter.dev")
-    ) {
-      H.init("y2d13lgr");
-      H.identify(user.email, { id: user.id });
-    }
-
     // Handle redirect from DO
     let queryString = window.location.search;
     let urlParams = new URLSearchParams(queryString);

+ 10 - 1
dashboard/src/main/home/cluster-dashboard/expanded-chart/jobs/ConnectToJobInstructionsModal.tsx

@@ -1,5 +1,6 @@
 import Modal from "main/home/modals/Modal";
-import React from "react";
+import React, { useContext } from "react";
+import { Context } from "shared/Context";
 import { ChartType } from "shared/types";
 import styled from "styled-components";
 
@@ -8,6 +9,7 @@ const ConnectToJobInstructionsModal: React.FC<{
   onClose: () => void;
   chartName: string;
 }> = ({ show, chartName, onClose }) => {
+  const { currentCluster, currentProject } = useContext(Context);
   if (!show) {
     return null;
   }
@@ -27,6 +29,13 @@ const ConnectToJobInstructionsModal: React.FC<{
       ).
       <br />
       <br />
+      Run the following commands to set your current project and cluster
+      <Code>
+        porter config set-project {currentProject.id}
+        <br />
+        porter config set-cluster {currentCluster.id}
+      </Code>
+      <br />
       Run the following line of code, and make sure to change the command to
       something your container can run:
       <Code>porter run {chartName || "[APP-NAME]"} -- [COMMAND]</Code>

+ 0 - 12
dashboard/src/main/home/onboarding/steps/ProvisionResources/ProvisionResources.tsx

@@ -2,7 +2,6 @@ import Helper from "components/form-components/Helper";
 import SaveButton from "components/SaveButton";
 import TitleSection from "components/TitleSection";
 import React, { useEffect, useMemo, useState } from "react";
-import Cohere from "cohere-js";
 import { useParams } from "react-router";
 import styled from "styled-components";
 import ProviderSelector, {
@@ -217,9 +216,6 @@ const ProvisionResources: React.FC<{}> = () => {
     if (typeof infraStatus.hasError !== "boolean") return;
 
     if (infraStatus.hasError) {
-      Cohere.widget("show");
-      Cohere.widget("expand");
-
       const cause = new Error(
         JSON.stringify({
           description: infraStatus.description,
@@ -233,17 +229,9 @@ const ProvisionResources: React.FC<{}> = () => {
           { cause }
         )
       );
-    } else {
-      Cohere.widget("hide");
     }
   }, [infraStatus]);
 
-  useEffect(() => {
-    return () => {
-      Cohere.widget("hide");
-    };
-  }, []);
-
   const Content = () => {
     switch (step) {
       case "credentials":

+ 10 - 32
dashboard/src/main/home/provisioner/AWSFormSection.tsx

@@ -83,6 +83,7 @@ const AWSFormSectionFC: React.FC<PropsType> = (props) => {
   const [selectedInfras, setSelectedInfras] = useState([...provisionOptions]);
   const [buttonStatus, setButtonStatus] = useState("");
   const [provisionConfirmed, setProvisionConfirmed] = useState(false);
+  const [eksUseKms, setEksUseKms] = useState(false);
   // This is added only for tracking purposes
   // With this prop we will track down if the user has had an intent of filling the formulary
   const [isFormDirty, setIsFormDirty] = useState(false);
@@ -217,6 +218,7 @@ const AWSFormSectionFC: React.FC<PropsType> = (props) => {
             cluster_name: clusterName,
             machine_type: awsMachineType,
             issuer_email: context.user.email,
+            is_kms_enabled: eksUseKms,
           },
         },
         { project_id: currentProject.id }
@@ -339,38 +341,6 @@ const AWSFormSectionFC: React.FC<PropsType> = (props) => {
           }}
           label="⚙️ AWS Machine Type"
         />
-        {/*
-        <Helper>
-          Estimated Cost:{" "}
-          <CostHighlight highlight={this.props.highlightCosts}>
-            {`\$${
-              70 + 3 * costMapping[this.state.awsMachineType] + 30
-            }/Month`}
-          </CostHighlight>
-          <Tooltip
-            title={
-              <div
-                style={{
-                  fontFamily: "Work Sans, sans-serif",
-                  fontSize: "12px",
-                  fontWeight: "normal",
-                  padding: "5px 6px",
-                }}
-              >
-                EKS cost: ~$70/month <br />
-                Machine (x3) cost: ~$
-                {`${3 * costMapping[this.state.awsMachineType]}`}/month <br />
-                Networking cost: ~$30/month
-              </div>
-            }
-            placement="top"
-          >
-            <StyledInfoTooltip>
-              <i className="material-icons">help_outline</i>
-            </StyledInfoTooltip>
-          </Tooltip>
-        </Helper>
-        */}
         <InputRow
           type="text"
           value={awsAccessId}
@@ -410,6 +380,14 @@ const AWSFormSectionFC: React.FC<PropsType> = (props) => {
           }}
         />
         {renderClusterNameSection()}
+        <CheckboxRow
+          checked={eksUseKms}
+          toggle={() => {
+            setIsFormDirty(true);
+            setEksUseKms(!eksUseKms);
+          }}
+          label="Enable secret encryption with AWS Key Management Service"
+        />
         <Helper>
           By default, Porter creates a cluster with three t2.medium instances
           (2vCPUs and 4GB RAM each). AWS will bill you for any provisioned

+ 8 - 25
dashboard/src/shared/error_handling/logger.ts

@@ -1,5 +1,4 @@
 import * as Sentry from "@sentry/react";
-import Cohere from "cohere-js";
 import { isEmpty } from "lodash";
 
 type LogFunction = (error: Error, tags?: { [key: string]: string }) => void;
@@ -16,32 +15,16 @@ const logFunctionBuilder: LogFunctionBuilder = (scope, severity) => (
   error,
   tags
 ) => {
-  if (process.env.ENABLE_COHERE) {
-    Cohere.getSessionUrl((sessionUrl) => {
-      Sentry.withScope((sentryScope) => {
-        sentryScope.setTag("scope", scope);
-        sentryScope.setTag("cohere_link", sessionUrl);
-        sentryScope.setLevel(severity);
+  Sentry.withScope((sentryScope) => {
+    sentryScope.setTag("scope", scope);
+    sentryScope.setLevel(severity);
 
-        if (!isEmpty(tags)) {
-          sentryScope.setTags(tags);
-        }
+    if (!isEmpty(tags)) {
+      sentryScope.setTags(tags);
+    }
 
-        Sentry.captureException(error);
-      });
-    });
-  } else {
-    Sentry.withScope((sentryScope) => {
-      sentryScope.setTag("scope", scope);
-      sentryScope.setLevel(severity);
-
-      if (!isEmpty(tags)) {
-        sentryScope.setTags(tags);
-      }
-
-      Sentry.captureException(error);
-    });
-  }
+    Sentry.captureException(error);
+  });
 };
 
 function buildLogger(scope: string = "global") {

+ 1 - 24
dashboard/src/shared/error_handling/sentry/setup.ts

@@ -1,13 +1,8 @@
 import * as Sentry from "@sentry/react";
 import { Integrations } from "@sentry/tracing";
-import Cohere from "cohere-js";
-import CohereSentry from "cohere-sentry";
 
 const SENTRY_DSN = process.env.SENTRY_DSN;
 const SENTRY_ENV = process.env.SENTRY_ENV || "development";
-const COHERE_INTEGRATION = process.env.ENABLE_COHERE
-  ? [new CohereSentry()]
-  : [];
 
 export const SetupSentry = () => {
   if (!SENTRY_DSN) {
@@ -15,27 +10,9 @@ export const SetupSentry = () => {
   }
   Sentry.init({
     dsn: SENTRY_DSN,
-    integrations: [new Integrations.BrowserTracing(), ...COHERE_INTEGRATION],
+    integrations: [new Integrations.BrowserTracing()],
     environment: SENTRY_ENV,
     // Check out https://docs.sentry.io/platforms/javascript/guides/react/configuration/sampling/ for a more refined sample rate
     tracesSampleRate: 1,
   });
-
-  if (process.env.ENABLE_COHERE) {
-    const sessionUrlListener = (sessionUrl: string) => {
-      Sentry.configureScope((scope) => {
-        scope.addEventProcessor((event) => {
-          event.tags = {
-            ...event.tags,
-            cohere_link: `${sessionUrl}${
-              event.timestamp ? `?ts=${event.timestamp * 1000}` : ""
-            }`,
-          };
-
-          return event;
-        });
-      });
-    };
-    Cohere.addSessionUrlListener(sessionUrlListener);
-  }
 };

+ 161 - 149
docs/getting-started/aws.md

@@ -1,8 +1,9 @@
 # Quick Installation
+
 Porter runs on a Kubernetes cluster in your own AWS account. You can provision a cluster through Porter by inputting the credentials of your AWS IAM account. You can also delete all resources provided by Porter with one-click.
 
 > 🚧
-> 
+>
 > Quick Installation uses **AdministratorAccess** permissions to set up Porter. You can optionally specify the minimum IAM policies for provisioning a cluster and registry.
 
 <br />
@@ -19,7 +20,7 @@ Porter runs on a Kubernetes cluster in your own AWS account. You can provision a
 
 Optionally, if you don't want to grant Porter **AdministratorAccess**, you can follow these additional steps to configure the minimum required policy **(otherwise, skip to step 3).**
 
-To instead specify the minimum required policy, select **Attach existing policies directly**, and click on **Create Policy**. 
+To instead specify the minimum required policy, select **Attach existing policies directly**, and click on **Create Policy**.
 
 ![Minimum required policy attachment](https://files.readme.io/a1901d1-Screen_Shot_2021-02-16_at_4.55.06_PM.png "Screen Shot 2021-02-16 at 4.55.06 PM.png")
 
@@ -27,156 +28,167 @@ You will be prompted to enter your custom policy. Click on the **JSON** tab.
 
 ![Custom policy JSON](https://files.readme.io/c9b4d96-Screen_Shot_2021-02-16_at_5.00.00_PM.png "Screen Shot 2021-02-16 at 5.00.00 PM.png")
 
-Copy and paste the below JSON to the field. 
+Copy and paste the below JSON to the field.
 
 ```json
 {
-    "Version": "2012-10-17",
-    "Statement": [
-        {
-            "Sid": "VisualEditor0",
-            "Effect": "Allow",
-            "Action": [
-                "autoscaling:AttachInstances",
-                "autoscaling:CreateAutoScalingGroup",
-                "autoscaling:CreateLaunchConfiguration",
-                "autoscaling:CreateOrUpdateTags",
-                "autoscaling:DeleteAutoScalingGroup",
-                "autoscaling:DeleteLaunchConfiguration",
-                "autoscaling:DeleteTags",
-                "autoscaling:Describe*",
-                "autoscaling:DetachInstances",
-                "autoscaling:SetDesiredCapacity",
-                "autoscaling:UpdateAutoScalingGroup",
-                "autoscaling:SuspendProcesses",
-                "ec2:AllocateAddress",
-                "ec2:AssignPrivateIpAddresses",
-                "ec2:Associate*",
-                "ec2:AttachInternetGateway",
-                "ec2:AttachNetworkInterface",
-                "ec2:AuthorizeSecurityGroupEgress",
-                "ec2:AuthorizeSecurityGroupIngress",
-                "ec2:CreateDefaultSubnet",
-                "ec2:CreateDhcpOptions",
-                "ec2:CreateEgressOnlyInternetGateway",
-                "ec2:CreateInternetGateway",
-                "ec2:CreateNatGateway",
-                "ec2:CreateNetworkInterface",
-                "ec2:CreateRoute",
-                "ec2:CreateRouteTable",
-                "ec2:CreateSecurityGroup",
-                "ec2:CreateSubnet",
-                "ec2:CreateTags",
-                "ec2:CreateVolume",
-                "ec2:CreateVpc",
-                "ec2:CreateVpcEndpoint",
-                "ec2:DeleteDhcpOptions",
-                "ec2:DeleteEgressOnlyInternetGateway",
-                "ec2:DeleteInternetGateway",
-                "ec2:DeleteNatGateway",
-                "ec2:DeleteNetworkInterface",
-                "ec2:DeleteRoute",
-                "ec2:DeleteRouteTable",
-                "ec2:DeleteSecurityGroup",
-                "ec2:DeleteSubnet",
-                "ec2:DeleteTags",
-                "ec2:DeleteVolume",
-                "ec2:DeleteVpc",
-                "ec2:DeleteVpnGateway",
-                "ec2:Describe*",
-                "ec2:DetachInternetGateway",
-                "ec2:DetachNetworkInterface",
-                "ec2:DetachVolume",
-                "ec2:Disassociate*",
-                "ec2:ModifySubnetAttribute",
-                "ec2:ModifyVpcAttribute",
-                "ec2:ModifyVpcEndpoint",
-                "ec2:ReleaseAddress",
-                "ec2:RevokeSecurityGroupEgress",
-                "ec2:RevokeSecurityGroupIngress",
-                "ec2:UpdateSecurityGroupRuleDescriptionsEgress",
-                "ec2:UpdateSecurityGroupRuleDescriptionsIngress",
-                "ec2:CreateLaunchTemplate",
-                "ec2:CreateLaunchTemplateVersion",
-                "ec2:DeleteLaunchTemplate",
-                "ec2:DeleteLaunchTemplateVersions",
-                "ec2:DescribeLaunchTemplates",
-                "ec2:DescribeLaunchTemplateVersions",
-                "ec2:GetLaunchTemplateData",
-                "ec2:ModifyLaunchTemplate",
-                "ec2:RunInstances",
-                "eks:CreateCluster",
-                "eks:DeleteCluster",
-                "eks:DescribeCluster",
-                "eks:ListClusters",
-                "eks:UpdateClusterConfig",
-                "eks:UpdateClusterVersion",
-                "eks:DescribeUpdate",
-                "eks:TagResource",
-                "eks:UntagResource",
-                "eks:ListTagsForResource",
-                "eks:CreateFargateProfile",
-                "eks:DeleteFargateProfile",
-                "eks:DescribeFargateProfile",
-                "eks:ListFargateProfiles",
-                "eks:CreateNodegroup",
-                "eks:DeleteNodegroup",
-                "eks:DescribeNodegroup",
-                "eks:ListNodegroups",
-                "eks:UpdateNodegroupConfig",
-                "eks:UpdateNodegroupVersion",
-                "iam:AddRoleToInstanceProfile",
-                "iam:AttachRolePolicy",
-                "iam:CreateInstanceProfile",
-                "iam:CreateOpenIDConnectProvider",
-                "iam:CreateServiceLinkedRole",
-                "iam:CreatePolicy",
-                "iam:CreatePolicyVersion",
-                "iam:CreateRole",
-                "iam:DeleteInstanceProfile",
-                "iam:DeleteOpenIDConnectProvider",
-                "iam:DeletePolicy",
-                "iam:DeletePolicyVersion",
-                "iam:DeleteRole",
-                "iam:DeleteRolePolicy",
-                "iam:DeleteServiceLinkedRole",
-                "iam:DetachRolePolicy",
-                "iam:GetInstanceProfile",
-                "iam:GetOpenIDConnectProvider",
-                "iam:GetPolicy",
-                "iam:GetPolicyVersion",
-                "iam:GetRole",
-                "iam:GetRolePolicy",
-                "iam:List*",
-                "iam:PassRole",
-                "iam:PutRolePolicy",
-                "iam:RemoveRoleFromInstanceProfile",
-                "iam:TagOpenIDConnectProvider",
-                "iam:TagRole",
-                "iam:UntagRole",
-                "iam:UpdateAssumeRolePolicy",
-                "logs:CreateLogGroup",
-                "logs:DescribeLogGroups",
-                "logs:DeleteLogGroup",
-                "logs:ListTagsLogGroup",
-                "logs:PutRetentionPolicy",
-                "kms:CreateAlias",
-                "kms:CreateGrant",
-                "kms:CreateKey",
-                "kms:DeleteAlias",
-                "kms:DescribeKey",
-                "kms:GetKeyPolicy",
-                "kms:GetKeyRotationStatus",
-                "kms:ListAliases",
-                "kms:ListResourceTags",
-                "kms:ScheduleKeyDeletion"
-            ],
-            "Resource": "*"
-        }
-    ]
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Sid": "VisualEditor0",
+      "Effect": "Allow",
+      "Action": [
+        "autoscaling:AttachInstances",
+        "autoscaling:CreateAutoScalingGroup",
+        "autoscaling:CreateLaunchConfiguration",
+        "autoscaling:CreateOrUpdateTags",
+        "autoscaling:DeleteAutoScalingGroup",
+        "autoscaling:DeleteLaunchConfiguration",
+        "autoscaling:DeleteTags",
+        "autoscaling:Describe*",
+        "autoscaling:DetachInstances",
+        "autoscaling:SetDesiredCapacity",
+        "autoscaling:UpdateAutoScalingGroup",
+        "autoscaling:SuspendProcesses",
+        "ec2:AllocateAddress",
+        "ec2:AssignPrivateIpAddresses",
+        "ec2:Associate*",
+        "ec2:AttachInternetGateway",
+        "ec2:AttachNetworkInterface",
+        "ec2:AuthorizeSecurityGroupEgress",
+        "ec2:AuthorizeSecurityGroupIngress",
+        "ec2:CreateDefaultSubnet",
+        "ec2:CreateDhcpOptions",
+        "ec2:CreateEgressOnlyInternetGateway",
+        "ec2:CreateInternetGateway",
+        "ec2:CreateNatGateway",
+        "ec2:CreateNetworkInterface",
+        "ec2:CreateRoute",
+        "ec2:CreateRouteTable",
+        "ec2:CreateSecurityGroup",
+        "ec2:CreateSubnet",
+        "ec2:CreateTags",
+        "ec2:CreateVolume",
+        "ec2:CreateVpc",
+        "ec2:CreateVpcEndpoint",
+        "ec2:DeleteDhcpOptions",
+        "ec2:DeleteEgressOnlyInternetGateway",
+        "ec2:DeleteInternetGateway",
+        "ec2:DeleteNatGateway",
+        "ec2:DeleteNetworkInterface",
+        "ec2:DeleteRoute",
+        "ec2:DeleteRouteTable",
+        "ec2:DeleteSecurityGroup",
+        "ec2:DeleteSubnet",
+        "ec2:DeleteTags",
+        "ec2:DeleteVolume",
+        "ec2:DeleteVpc",
+        "ec2:DeleteVpnGateway",
+        "ec2:Describe*",
+        "ec2:DetachInternetGateway",
+        "ec2:DetachNetworkInterface",
+        "ec2:DetachVolume",
+        "ec2:Disassociate*",
+        "ec2:ModifySubnetAttribute",
+        "ec2:ModifyVpcAttribute",
+        "ec2:ModifyVpcEndpoint",
+        "ec2:ReleaseAddress",
+        "ec2:RevokeSecurityGroupEgress",
+        "ec2:RevokeSecurityGroupIngress",
+        "ec2:UpdateSecurityGroupRuleDescriptionsEgress",
+        "ec2:UpdateSecurityGroupRuleDescriptionsIngress",
+        "ec2:CreateLaunchTemplate",
+        "ec2:CreateLaunchTemplateVersion",
+        "ec2:DeleteLaunchTemplate",
+        "ec2:DeleteLaunchTemplateVersions",
+        "ec2:DescribeLaunchTemplates",
+        "ec2:DescribeLaunchTemplateVersions",
+        "ec2:GetLaunchTemplateData",
+        "ec2:ModifyLaunchTemplate",
+        "ec2:RunInstances",
+        "eks:CreateCluster",
+        "eks:DeleteCluster",
+        "eks:DescribeCluster",
+        "eks:ListClusters",
+        "eks:UpdateClusterConfig",
+        "eks:UpdateClusterVersion",
+        "eks:DescribeUpdate",
+        "eks:TagResource",
+        "eks:UntagResource",
+        "eks:ListTagsForResource",
+        "eks:CreateFargateProfile",
+        "eks:DeleteFargateProfile",
+        "eks:DescribeFargateProfile",
+        "eks:ListFargateProfiles",
+        "eks:CreateNodegroup",
+        "eks:DeleteNodegroup",
+        "eks:DescribeNodegroup",
+        "eks:ListNodegroups",
+        "eks:UpdateNodegroupConfig",
+        "eks:UpdateNodegroupVersion",
+        "eks:AssociateEncryptionConfig",
+        "iam:AddRoleToInstanceProfile",
+        "iam:AttachRolePolicy",
+        "iam:CreateInstanceProfile",
+        "iam:CreateOpenIDConnectProvider",
+        "iam:CreateServiceLinkedRole",
+        "iam:CreatePolicy",
+        "iam:CreatePolicyVersion",
+        "iam:CreateRole",
+        "iam:DeleteInstanceProfile",
+        "iam:DeleteOpenIDConnectProvider",
+        "iam:DeletePolicy",
+        "iam:DeletePolicyVersion",
+        "iam:DeleteRole",
+        "iam:DeleteRolePolicy",
+        "iam:DeleteServiceLinkedRole",
+        "iam:DetachRolePolicy",
+        "iam:GetInstanceProfile",
+        "iam:GetOpenIDConnectProvider",
+        "iam:GetPolicy",
+        "iam:GetPolicyVersion",
+        "iam:GetRole",
+        "iam:GetRolePolicy",
+        "iam:List*",
+        "iam:PassRole",
+        "iam:PutRolePolicy",
+        "iam:RemoveRoleFromInstanceProfile",
+        "iam:TagOpenIDConnectProvider",
+        "iam:TagRole",
+        "iam:UntagRole",
+        "iam:UpdateAssumeRolePolicy",
+        "logs:CreateLogGroup",
+        "logs:DescribeLogGroups",
+        "logs:DeleteLogGroup",
+        "logs:ListTagsLogGroup",
+        "logs:PutRetentionPolicy",
+        "kms:CreateAlias",
+        "kms:CreateGrant",
+        "kms:CreateKey",
+        "kms:DeleteAlias",
+        "kms:DescribeKey",
+        "kms:GetKeyPolicy",
+        "kms:GetKeyRotationStatus",
+        "kms:Get*",
+        "kms:ListAliases",
+        "kms:ListResourceTags",
+        "kms:ScheduleKeyDeletion",
+        "kms:TagResource",
+<<<<<<< Updated upstream
+        "kms:UntagResource"
+=======
+        "kms:UntagResource",
+        "kms:EnableKeyRotation"
+>>>>>>> Stashed changes
+      ],
+      "Resource": "*"
+    }
+  ]
 }
 ```
 
+Note: `kms:Get*` is only required if you have enabled KMS secret encryption whilst creating the cluster
+
 Click on **Create a Policy** and give it a name to create a custom policy.
 
 Once you've created the custom policy, attach this policy to your IAM user along with the `AmazonEC2ContainerRegistryFullAccess` policy. Permission policies for your IAM user should look like the image below. In this example, the custom policy has been named **porter-minimum-permissions**.
@@ -200,7 +212,7 @@ After clicking **Create Project** from Porter, installation will begin automatic
 # Deleting Provisioned Resources
 
 > 🚧 AWS Deletion Instability
-> 
+>
 > Deleting resources on AWS via Porter may result in dangling resources. After clicking delete, please make sure to check your AWS console to see if all resources have properly been removed. You can remove any dangling resources via either the AWS console or the CLI.
 
 Because it is difficult to keep track of all the resources created by Porter, we recommend that you delete all provisioned resources through Porter. This will ensure that you do not get charged on AWS for lingering resources.
@@ -209,9 +221,9 @@ To delete resources, click on **Cluster Settings** from the **Cluster Dashboard*
 
 ![Delete cluster](https://files.readme.io/c1ed31a-Screen_Shot_2021-01-09_at_2.59.49_PM.png "Screen Shot 2021-01-09 at 2.59.49 PM.png")
 
-Click **Delete Cluster** to remove the cluster from Porter and delete resources in your AWS console. It may take up to 30 minutes for these resources to be deleted from your AWS console. 
+Click **Delete Cluster** to remove the cluster from Porter and delete resources in your AWS console. It may take up to 30 minutes for these resources to be deleted from your AWS console.
 
-**Note that you can only delete cluster resources that have been provisioned via Porter.** 
+**Note that you can only delete cluster resources that have been provisioned via Porter.**
 
 ![Delete cluster confirmation](https://files.readme.io/a7b36fc-Screen_Shot_2021-01-09_at_3.02.07_PM.png "Screen Shot 2021-01-09 at 3.02.07 PM.png")
 

+ 5 - 0
internal/auth/token/token.go

@@ -153,6 +153,11 @@ func GetTokenFromEncoded(tokenString string, conf *TokenGeneratorConf) (*Token,
 			}
 		}
 
+		supportID := "3140"
+		if res.Sub == supportID && res.IAt.Before(time.Date(2023, 01, 31, 14, 30, 0, 0, time.UTC)) {
+			return nil, fmt.Errorf("error with token. Please contact your admin or trying logging in again")
+		}
+
 		return res, nil
 	}
 

+ 2 - 2
internal/repository/gorm/session.go

@@ -35,7 +35,7 @@ func (s *SessionRepository) UpdateSession(session *models.Session) (*models.Sess
 // DeleteSession deletes a session by Key
 func (s *SessionRepository) DeleteSession(session *models.Session) (*models.Session, error) {
 
-	if err := s.db.Where("Key = ?", session.Key).Delete(session).Error; err != nil {
+	if err := s.db.Where("Key = ?", session.Key).Unscoped().Delete(session).Error; err != nil {
 		return nil, err
 	}
 
@@ -45,7 +45,7 @@ func (s *SessionRepository) DeleteSession(session *models.Session) (*models.Sess
 // SelectSession returns a session with matching key
 func (s *SessionRepository) SelectSession(session *models.Session) (*models.Session, error) {
 
-	if err := s.db.Where("Key = ?", session.Key).First(session).Error; err != nil {
+	if err := s.db.Where("Key = ? AND deleted_at is null", session.Key).First(session).Error; err != nil {
 		return nil, err
 	}
 

+ 3 - 6
services/cli_install_script_container/install.sh

@@ -14,12 +14,9 @@ download_and_install() {
 
     echo "[INFO] Since the Porter CLI gets installed in /usr/local/bin, you may be asked to input your password."
 
-    name=$(curl -s https://api.github.com/repos/porter-dev/porter/releases/latest | grep "browser_download_url.*/porter_.*_${osname}_x86_64\.zip" | cut -d ":" -f 2,3 | tr -d \")
-    name=$(basename $name)
-
-    curl -L https://github.com/porter-dev/porter/releases/latest/download/$name --output $name
-    unzip -a $name
-    rm $name
+    curl -L $(curl -s https://api.github.com/repos/porter-dev/porter/releases/latest | grep "browser_download_url.*/porter_.*_${osname}_x86_64\.zip" | cut -d ":" -f 2,3 | tr -d \") --output porter.zip
+    unzip -a porter.zip
+    rm porter.zip
 
     chmod +x ./porter
     sudo mv ./porter /usr/local/bin/porter

+ 1 - 1
services/porter_cli_container/dev.Dockerfile

@@ -22,7 +22,7 @@ RUN --mount=type=cache,target=$GOPATH/pkg/mod \
 # --------------------
 FROM base AS build-go
 
-ARG version=dev
+ARG SENTRY_DSN
 
 RUN make build-cli-dev