From 3194a03a06e1e66f94e48639655d38f2065701fb Mon Sep 17 00:00:00 2001 From: Lukasz Zajaczkowski Date: Mon, 7 Oct 2024 15:18:57 +0200 Subject: [PATCH 1/3] better streamline plural up --cloud --- cmd/command/cd/cd_clusters.go | 25 ++++++++++++++++--------- cmd/command/up/up.go | 32 ++++++++++++++++++++++++++++++-- pkg/manifest/manifest.go | 10 ++++++---- pkg/provider/aws.go | 23 +++++++++++++---------- pkg/provider/azure.go | 2 +- pkg/provider/equinix.go | 2 +- pkg/provider/gcp.go | 2 +- pkg/provider/kind.go | 2 +- pkg/provider/linode.go | 2 +- 9 files changed, 70 insertions(+), 30 deletions(-) diff --git a/cmd/command/cd/cd_clusters.go b/cmd/command/cd/cd_clusters.go index b5afad5b..3d5bd9e4 100644 --- a/cmd/command/cd/cd_clusters.go +++ b/cmd/command/cd/cd_clusters.go @@ -118,19 +118,12 @@ func (p *Plural) cdClusterCommands() []cli.Command { } func (p *Plural) handleListClusters(_ *cli.Context) error { - if err := p.InitConsoleClient(consoleToken, consoleURL); err != nil { - return err - } - - clusters, err := p.ConsoleClient.ListClusters() + clusters, err := p.ListClusters() if err != nil { return err } - if clusters == nil { - return fmt.Errorf("returned objects list [ListClusters] is nil") - } headers := []string{"Id", "Name", "Handle", "Version", "Provider"} - return utils.PrintTable(clusters.Clusters.Edges, headers, func(cl *gqlclient.ClusterEdgeFragment) ([]string, error) { + return utils.PrintTable(clusters, headers, func(cl *gqlclient.ClusterEdgeFragment) ([]string, error) { provider := "" if cl.Node.Provider != nil { provider = cl.Node.Provider.Name @@ -147,6 +140,20 @@ func (p *Plural) handleListClusters(_ *cli.Context) error { }) } +func (p *Plural) ListClusters() ([]*gqlclient.ClusterEdgeFragment, error) { + if err := p.InitConsoleClient(consoleToken, consoleURL); err != nil { + return nil, err + } + clusters, err := p.ConsoleClient.ListClusters() + if err != nil { + return nil, err + } + if clusters == nil { + return nil, fmt.Errorf("returned objects list [ListClusters] is nil") + } + return clusters.Clusters.Edges, nil +} + func (p *Plural) GetClusterId(handle string) (string, string, error) { if err := p.InitConsoleClient(consoleToken, consoleURL); err != nil { return "", "", err diff --git a/cmd/command/up/up.go b/cmd/command/up/up.go index 4795650a..fd1405ac 100644 --- a/cmd/command/up/up.go +++ b/cmd/command/up/up.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "github.com/AlecAivazis/survey/v2" "github.com/pluralsh/plural-cli/cmd/command/cd" "github.com/pluralsh/plural-cli/pkg/client" "github.com/pluralsh/plural-cli/pkg/common" @@ -81,11 +82,10 @@ func (p *Plural) handleUp(c *cli.Context) error { } if c.Bool("cloud") { - id, name, err := cd.GetClusterId("mgmt") + id, name, err := getCluster(cd) if err != nil { return err } - ctx.ImportCluster = lo.ToPtr(id) ctx.CloudCluster = name } @@ -119,3 +119,31 @@ func (p *Plural) handleUp(c *cli.Context) error { utils.Highlight("Feel free to use terraform as you normally would, and leverage the gitops setup we've generated in the apps/ subfolder\n") return nil } + +func getCluster(cd *cd.Plural) (id string, name string, err error) { + if cd == nil { + return "", "", fmt.Errorf("please provide a plural client") + } + clusters, err := cd.ListClusters() + if err != nil { + return "", "", err + } + + clusterNames := []string{} + clusterMap := map[string]string{} + + for _, cluster := range clusters { + clusterNames = append(clusterNames, cluster.Node.Name) + clusterMap[cluster.Node.Name] = cluster.Node.ID + } + + prompt := &survey.Select{ + Message: "Select one of the following clusters:", + Options: clusterNames, + } + if err = survey.AskOne(prompt, &name, survey.WithValidator(survey.Required)); err != nil { + return + } + id = clusterMap[name] + return +} diff --git a/pkg/manifest/manifest.go b/pkg/manifest/manifest.go index d720fa96..7b4c0882 100644 --- a/pkg/manifest/manifest.go +++ b/pkg/manifest/manifest.go @@ -118,14 +118,16 @@ func Read(path string) (man *Manifest, err error) { return } -func (pMan *ProjectManifest) Configure(cloud bool) Writer { +func (pMan *ProjectManifest) Configure(cloud bool, cluster string) Writer { utils.Highlight("\nLet's get some final information about your workspace set up\n\n") - res, _ := utils.ReadAlphaNum("Give us a unique, memorable string to use for bucket naming, eg an abbreviation for your company: ") - pMan.BucketPrefix = res - pMan.Bucket = fmt.Sprintf("%s-tf-state", res) + pMan.BucketPrefix = cluster + pMan.Bucket = fmt.Sprintf("plrl-cloud-%s", cluster) if !cloud { + res, _ := utils.ReadAlphaNum("Give us a unique, memorable string to use for bucket naming, eg an abbreviation for your company: ") + pMan.BucketPrefix = res + pMan.Bucket = fmt.Sprintf("%s-tf-state", res) if err := pMan.ConfigureNetwork(); err != nil { return nil } diff --git a/pkg/provider/aws.go b/pkg/provider/aws.go index dbdac54c..5e428da8 100644 --- a/pkg/provider/aws.go +++ b/pkg/provider/aws.go @@ -109,7 +109,7 @@ func mkAWS(conf config.Config) (provider *AWSProvider, err error) { provider.project = account provider.storageClient = client - azones, err := getAvailabilityZones(ctx, provider.Region()) + azones, err := getAvailabilityZones(ctx, provider.Region(), cloudFlag) if err != nil { return } @@ -123,7 +123,7 @@ func mkAWS(conf config.Config) (provider *AWSProvider, err error) { Owner: &manifest.Owner{Email: conf.Email, Endpoint: conf.Endpoint}, } - provider.writer = projectManifest.Configure(cloudFlag) + provider.writer = projectManifest.Configure(cloudFlag, provider.Cluster()) provider.bucket = projectManifest.Bucket return } @@ -159,17 +159,20 @@ func getEC2Client(ctx context.Context, region string) (*ec2.Client, error) { } // TODO: during Plural init we should ask the user to choose which AZs they want to use (first 3, random, manual, look at how CAPA does that). There should be a minimum limit of 3. -func getAvailabilityZones(ctx context.Context, region string) ([]string, error) { +func getAvailabilityZones(ctx context.Context, region string, cloudFlag bool) ([]string, error) { first3 := "first three" random := "random" manual := "manual" - choice := "" - prompt := &survey.Select{ - Message: "Which availability zones you would like to use:", - Options: []string{first3, random, manual}, - } - if err := survey.AskOne(prompt, &choice); err != nil { - return nil, err + choice := first3 + if !cloudFlag { + choice = "" + prompt := &survey.Select{ + Message: "Which availability zones you would like to use:", + Options: []string{first3, random, manual}, + } + if err := survey.AskOne(prompt, &choice); err != nil { + return nil, err + } } switch choice { diff --git a/pkg/provider/azure.go b/pkg/provider/azure.go index 41f65beb..fc83c024 100644 --- a/pkg/provider/azure.go +++ b/pkg/provider/azure.go @@ -183,7 +183,7 @@ func mkAzure(conf config.Config) (prov *AzureProvider, err error) { Context: prov.Context(), Owner: &manifest.Owner{Email: conf.Email, Endpoint: conf.Endpoint}, } - prov.writer = projectManifest.Configure(cloudFlag) + prov.writer = projectManifest.Configure(cloudFlag, prov.Cluster()) prov.bucket = projectManifest.Bucket return } diff --git a/pkg/provider/equinix.go b/pkg/provider/equinix.go index 96a1ed4d..40fe3b0b 100644 --- a/pkg/provider/equinix.go +++ b/pkg/provider/equinix.go @@ -108,7 +108,7 @@ func mkEquinix(conf config.Config) (provider *EQUINIXProvider, err error) { Owner: &manifest.Owner{Email: conf.Email, Endpoint: conf.Endpoint}, } - provider.writer = projectManifest.Configure(cloudFlag) + provider.writer = projectManifest.Configure(cloudFlag, provider.Cluster()) provider.bucket = projectManifest.Bucket return } diff --git a/pkg/provider/gcp.go b/pkg/provider/gcp.go index 2beb9e48..bbbdb24e 100644 --- a/pkg/provider/gcp.go +++ b/pkg/provider/gcp.go @@ -160,7 +160,7 @@ func mkGCP(conf config.Config) (provider *GCPProvider, err error) { Owner: &manifest.Owner{Email: conf.Email, Endpoint: conf.Endpoint}, } - provider.writer = projectManifest.Configure(cloudFlag) + provider.writer = projectManifest.Configure(cloudFlag, provider.Cluster()) provider.bucket = projectManifest.Bucket return } diff --git a/pkg/provider/kind.go b/pkg/provider/kind.go index 302b2650..e8f5782b 100644 --- a/pkg/provider/kind.go +++ b/pkg/provider/kind.go @@ -62,7 +62,7 @@ func mkKind(conf config.Config) (provider *KINDProvider, err error) { Owner: &manifest.Owner{Email: conf.Email, Endpoint: conf.Endpoint}, } - provider.writer = projectManifest.Configure(cloudFlag) + provider.writer = projectManifest.Configure(cloudFlag, provider.Cluster()) provider.bucket = projectManifest.Bucket return } diff --git a/pkg/provider/linode.go b/pkg/provider/linode.go index e92dae29..bb2c879f 100644 --- a/pkg/provider/linode.go +++ b/pkg/provider/linode.go @@ -87,7 +87,7 @@ func mkLinode(conf config.Config) (provider *LinodeProvider, err error) { Owner: &manifest.Owner{Email: conf.Email, Endpoint: conf.Endpoint}, } - provider.writer = projectManifest.Configure(cloudFlag) + provider.writer = projectManifest.Configure(cloudFlag, provider.Cluster()) provider.bucket = projectManifest.Bucket return } From b7559b5ef1aa70ca8c69e8659dc2e8b342236b59 Mon Sep 17 00:00:00 2001 From: michaeljguarino Date: Mon, 7 Oct 2024 12:00:36 -0400 Subject: [PATCH 2/3] disable cloud image builds to reduce security tab spam --- .github/workflows/ci.yaml | 134 +++++++++++++++++------------------ dockerfiles/Dockerfile.cloud | 6 +- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 70f73456..575dd302 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -86,73 +86,73 @@ jobs: # docker buildx stop ${{ steps.builder.outputs.name }} # sleep 10 # docker buildx rm ${{ steps.builder.outputs.name }} - cloud: - name: Build cloud image - runs-on: ubuntu-latest - permissions: - contents: 'read' - id-token: 'write' - packages: 'write' - security-events: write - actions: read - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 - with: - # list of Docker images to use as base name for tags - images: | - ghcr.io/pluralsh/plural-cli-cloud - # generate Docker tags based on the following events/attributes - tags: | - type=sha - type=ref,event=pr - type=ref,event=branch - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to GHCR - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Get current date - id: date - run: echo "date=$(date -u +'%Y-%m-%dT%H:%M:%S%z')" >> $GITHUB_OUTPUT - - uses: docker/build-push-action@v6 - with: - context: . - file: ./dockerfiles/Dockerfile.cloud - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64 - # cache-from: type=gha - # cache-to: type=gha,mode=max - build-args: | - APP_VSN=dev - APP_COMMIT=${{ github.sha }} - APP_DATE=${{ steps.date.outputs.date }} - - name: Run Trivy vulnerability scanner on cli cloud image - uses: aquasecurity/trivy-action@master - with: - scan-type: 'image' - image-ref: ${{ fromJSON(steps.meta.outputs.json).tags[0] }} - hide-progress: false - format: 'sarif' - output: 'trivy-results.sarif' - scanners: 'vuln' - timeout: 10m - ignore-unfixed: true - #severity: 'CRITICAL,HIGH' - - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: 'trivy-results.sarif' + # cloud: + # name: Build cloud image + # runs-on: ubuntu-latest + # permissions: + # contents: 'read' + # id-token: 'write' + # packages: 'write' + # security-events: write + # actions: read + # steps: + # - name: Checkout + # uses: actions/checkout@v3 + # - name: Docker meta + # id: meta + # uses: docker/metadata-action@v4 + # with: + # # list of Docker images to use as base name for tags + # images: | + # ghcr.io/pluralsh/plural-cli-cloud + # # generate Docker tags based on the following events/attributes + # tags: | + # type=sha + # type=ref,event=pr + # type=ref,event=branch + # - name: Set up QEMU + # uses: docker/setup-qemu-action@v3 + # - name: Set up Docker Buildx + # uses: docker/setup-buildx-action@v3 + # - name: Login to GHCR + # uses: docker/login-action@v2 + # with: + # registry: ghcr.io + # username: ${{ github.repository_owner }} + # password: ${{ secrets.GITHUB_TOKEN }} + # - name: Get current date + # id: date + # run: echo "date=$(date -u +'%Y-%m-%dT%H:%M:%S%z')" >> $GITHUB_OUTPUT + # - uses: docker/build-push-action@v6 + # with: + # context: . + # file: ./dockerfiles/Dockerfile.cloud + # push: true + # tags: ${{ steps.meta.outputs.tags }} + # labels: ${{ steps.meta.outputs.labels }} + # platforms: linux/amd64,linux/arm64 + # # cache-from: type=gha + # # cache-to: type=gha,mode=max + # build-args: | + # APP_VSN=dev + # APP_COMMIT=${{ github.sha }} + # APP_DATE=${{ steps.date.outputs.date }} + # - name: Run Trivy vulnerability scanner on cli cloud image + # uses: aquasecurity/trivy-action@master + # with: + # scan-type: 'image' + # image-ref: ${{ fromJSON(steps.meta.outputs.json).tags[0] }} + # hide-progress: false + # format: 'sarif' + # output: 'trivy-results.sarif' + # scanners: 'vuln' + # timeout: 10m + # ignore-unfixed: true + # #severity: 'CRITICAL,HIGH' + # - name: Upload Trivy scan results to GitHub Security tab + # uses: github/codeql-action/upload-sarif@v2 + # with: + # sarif_file: 'trivy-results.sarif' dind: name: Build dind image runs-on: ubuntu-latest diff --git a/dockerfiles/Dockerfile.cloud b/dockerfiles/Dockerfile.cloud index eac0d97b..c186f644 100644 --- a/dockerfiles/Dockerfile.cloud +++ b/dockerfiles/Dockerfile.cloud @@ -31,13 +31,13 @@ FROM alpine:3.17.2 as tools ARG TARGETARCH # renovate: datasource=github-releases depName=helm/helm -ENV HELM_VERSION=v3.10.3 +ENV HELM_VERSION=v3.15.1 # renovate: datasource=github-releases depName=hashicorp/terraform -ENV TERRAFORM_VERSION=v1.2.9 +ENV TERRAFORM_VERSION=v1.9.7 # renovate: datasource=github-tags depName=kubernetes/kubernetes -ENV KUBECTL_VERSION=v1.25.5 +ENV KUBECTL_VERSION=v1.30.0 RUN apk add --update --no-cache curl ca-certificates unzip wget openssl build-base && \ curl -L https://get.helm.sh/helm-${HELM_VERSION}-linux-${TARGETARCH}.tar.gz | tar xvz && \ From cea824cbfa8582ebd8d1d66cc22209cb0c565ab2 Mon Sep 17 00:00:00 2001 From: Lukasz Zajaczkowski Date: Tue, 8 Oct 2024 08:45:09 +0200 Subject: [PATCH 3/3] code review --- cmd/command/up/up.go | 2 +- pkg/provider/aws.go | 41 +++-------------------------------------- 2 files changed, 4 insertions(+), 39 deletions(-) diff --git a/cmd/command/up/up.go b/cmd/command/up/up.go index fd1405ac..6ac608be 100644 --- a/cmd/command/up/up.go +++ b/cmd/command/up/up.go @@ -122,7 +122,7 @@ func (p *Plural) handleUp(c *cli.Context) error { func getCluster(cd *cd.Plural) (id string, name string, err error) { if cd == nil { - return "", "", fmt.Errorf("please provide a plural client") + return "", "", fmt.Errorf("your CLI is not logged into Plural, try running `plural login` to generate local credentials") } clusters, err := cd.ListClusters() if err != nil { diff --git a/pkg/provider/aws.go b/pkg/provider/aws.go index 5e428da8..9fad9052 100644 --- a/pkg/provider/aws.go +++ b/pkg/provider/aws.go @@ -109,7 +109,7 @@ func mkAWS(conf config.Config) (provider *AWSProvider, err error) { provider.project = account provider.storageClient = client - azones, err := getAvailabilityZones(ctx, provider.Region(), cloudFlag) + azones, err := getAvailabilityZones(ctx, provider.Region()) if err != nil { return } @@ -158,44 +158,9 @@ func getEC2Client(ctx context.Context, region string) (*ec2.Client, error) { return ec2.NewFromConfig(cfg), nil } -// TODO: during Plural init we should ask the user to choose which AZs they want to use (first 3, random, manual, look at how CAPA does that). There should be a minimum limit of 3. -func getAvailabilityZones(ctx context.Context, region string, cloudFlag bool) ([]string, error) { - first3 := "first three" - random := "random" - manual := "manual" - choice := first3 - if !cloudFlag { - choice = "" - prompt := &survey.Select{ - Message: "Which availability zones you would like to use:", - Options: []string{first3, random, manual}, - } - if err := survey.AskOne(prompt, &choice); err != nil { - return nil, err - } - } - - switch choice { - case first3: - return fetchAZ(ctx, region, true) - case random: - return fetchAZ(ctx, region, false) - case manual: - text := "" - prompt := &survey.Multiline{ - Message: "Enter at least three availability zones ", - } - if err := survey.AskOne(prompt, &text); err != nil { - return nil, err - } - res := strings.Split(text, "\n") - if len(res) < 3 { - return nil, fmt.Errorf("expected at least three availability zones") - } - return res, nil - } +func getAvailabilityZones(ctx context.Context, region string) ([]string, error) { + return fetchAZ(ctx, region, true) - return nil, nil } func fetchAZ(context context.Context, region string, sorted bool) ([]string, error) {