From eabe8c0df267e47cecaef5c6e067aa84e4340332 Mon Sep 17 00:00:00 2001 From: michaeljguarino Date: Tue, 27 Aug 2024 04:07:44 -0400 Subject: [PATCH] Implement cloud flow for `plural up` (#530) * Implement cloud flow for `plural up` This adds a `--cloud` flag to plural up to create a console-less management cluster and register it w/ a hosted console. There are definitely some other ux things we should smooth here, but for now it's good. * fix go.mod * fix pluralctl setup * fix some minor bugs * Use temp dir to clone bootstrap repo in * improve cloud flag * fix after rebase --------- Co-authored-by: Lukasz Zajaczkowski --- cmd/command/cd/cd.go | 14 +++++- cmd/command/cd/cd_clusters.go | 19 ++++++-- cmd/command/up/backfill.go | 54 ++++++++++++++++++++++ cmd/command/up/up.go | 37 +++++++++++++-- go.mod | 4 +- go.sum | 8 ++-- pkg/api/client.go | 2 + pkg/api/cloud.go | 24 ++++++++++ pkg/client/plural.go | 2 +- pkg/common/app.go | 2 +- pkg/common/common.go | 5 ++- pkg/console/config.go | 12 ++--- pkg/console/console.go | 4 ++ pkg/manifest/manifest.go | 27 +++++------ pkg/provider/aws.go | 2 +- 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 +- pkg/provider/provider.go | 6 +++ pkg/test/mocks/Client.go | 48 ++++++++++++++++++++ pkg/up/context.go | 12 ++++- pkg/up/deploy.go | 29 +++++++++--- pkg/up/generate.go | 84 +++++++++++++++++++++++------------ pkg/up/prune.go | 4 ++ pkg/up/template.go | 9 +++- pkg/utils/git/repo.go | 5 +++ 28 files changed, 340 insertions(+), 83 deletions(-) create mode 100644 cmd/command/up/backfill.go create mode 100644 pkg/api/cloud.go diff --git a/cmd/command/cd/cd.go b/cmd/command/cd/cd.go index 1c4d8828..1b059bd2 100644 --- a/cmd/command/cd/cd.go +++ b/cmd/command/cd/cd.go @@ -106,7 +106,7 @@ func Commands(clients client.Plural, helmConfiguration *action.Configuration) [] }, { Name: "login", - Action: p.handleCdLogin, + Action: p.HandleCdLogin, Usage: "logs into your plural console", Flags: []cli.Flag{ cli.StringFlag{Name: "url", Usage: "console url"}, @@ -224,7 +224,17 @@ func confirmCluster(url, token string) (bool, error) { return common.Confirm(fmt.Sprintf("Are you sure you want to install deploy operator for the cluster:\nName: %s\nHandle: %s\nProvider: %s\n", myCluster.MyCluster.Name, handle, provider), "PLURAL_INSTALL_AGENT_CONFIRM"), nil } -func (p *Plural) handleCdLogin(c *cli.Context) (err error) { +func (p *Plural) HandleCdLogin(c *cli.Context) (err error) { + prior := console.ReadConfig() + if prior.Url != "" { + if common.Affirm( + fmt.Sprintf("You've already configured your console at %s, continue using those credentials?", prior.Url), + "PLURAL_CD_USE_EXISTING_CREDENTIALS", + ) { + return + } + } + url := c.String("url") if url == "" { url, err = utils.ReadLine("Enter the url of your console: ") diff --git a/cmd/command/cd/cd_clusters.go b/cmd/command/cd/cd_clusters.go index cd6dc2bf..2189fd5d 100644 --- a/cmd/command/cd/cd_clusters.go +++ b/cmd/command/cd/cd_clusters.go @@ -148,6 +148,19 @@ func (p *Plural) handleListClusters(_ *cli.Context) error { }) } +func (p *Plural) GetClusterId(handle string) (string, string, error) { + if err := p.InitConsoleClient(consoleToken, consoleURL); err != nil { + return "", "", err + } + + existing, err := p.ConsoleClient.GetCluster(nil, lo.ToPtr(handle)) + if err != nil { + return "", "", err + } + + return existing.ID, existing.Name, nil +} + func (p *Plural) handleDescribeCluster(c *cli.Context) error { if err := p.InitConsoleClient(consoleToken, consoleURL); err != nil { return err @@ -384,10 +397,10 @@ func (p *Plural) handleClusterReinstall(c *cli.Context) error { } id, name := common.GetIdAndName(c.Args().Get(0)) - return p.reinstallOperator(c, id, name) + return p.ReinstallOperator(c, id, name) } -func (p *Plural) reinstallOperator(c *cli.Context, id, handle *string) error { +func (p *Plural) ReinstallOperator(c *cli.Context, id, handle *string) error { deployToken, err := p.ConsoleClient.GetDeployToken(id, handle) if err != nil { return err @@ -446,7 +459,7 @@ func (p *Plural) handleClusterBootstrap(c *cli.Context) error { if attrs.Handle != nil { handle = attrs.Handle } - return p.reinstallOperator(c, nil, handle) + return p.ReinstallOperator(c, nil, handle) } return err diff --git a/cmd/command/up/backfill.go b/cmd/command/up/backfill.go new file mode 100644 index 00000000..f56a297f --- /dev/null +++ b/cmd/command/up/backfill.go @@ -0,0 +1,54 @@ +package up + +import ( + "fmt" + "strings" + + "encoding/base64" + + "github.com/pluralsh/gqlclient" + "github.com/pluralsh/plural-cli/pkg/console" + "github.com/pluralsh/plural-cli/pkg/crypto" + "github.com/samber/lo" +) + +func (p *Plural) backfillEncryption() error { + instances, err := p.Plural.Client.GetConsoleInstances() + if err != nil { + return err + } + + conf := console.ReadConfig() + + if conf.Url == "" { + return fmt.Errorf("You haven't configured your Plural Console client yet") + } + + var id string + for _, inst := range instances { + if strings.Contains(conf.Url, inst.URL) { + id = inst.ID + } + } + if id == "" { + return fmt.Errorf("Your configuration doesn't match to any existing Plural Console") + } + + prov, err := crypto.Build() + if err != nil { + return err + } + + raw, err := prov.SymmetricKey() + if err != nil { + return err + } + + encoded := base64.StdEncoding.EncodeToString(raw) + + return p.Plural.Client.UpdateConsoleInstance(id, gqlclient.ConsoleInstanceUpdateAttributes{ + Configuration: &gqlclient.ConsoleConfigurationUpdateAttributes{ + EncryptionKey: lo.ToPtr(encoded), + }, + }) +} diff --git a/cmd/command/up/up.go b/cmd/command/up/up.go index 4d5f9871..4795650a 100644 --- a/cmd/command/up/up.go +++ b/cmd/command/up/up.go @@ -2,12 +2,15 @@ package up import ( "fmt" + "os" + "github.com/pluralsh/plural-cli/cmd/command/cd" "github.com/pluralsh/plural-cli/pkg/client" "github.com/pluralsh/plural-cli/pkg/common" "github.com/pluralsh/plural-cli/pkg/up" "github.com/pluralsh/plural-cli/pkg/utils" "github.com/pluralsh/plural-cli/pkg/utils/git" + "github.com/samber/lo" "github.com/urfave/cli" ) @@ -35,6 +38,10 @@ func Command(clients client.Plural) cli.Command { Name: "ignore-preflights", Usage: "whether to ignore preflight check failures prior to init", }, + cli.BoolFlag{ + Name: "cloud", + Usage: "Whether you're provisioning against a cloud-hosted Plural Console", + }, cli.StringFlag{ Name: "commit", Usage: "commits your changes with this message", @@ -51,21 +58,45 @@ func (p *Plural) handleUp(c *cli.Context) error { } p.InitPluralClient() + cd := &cd.Plural{Plural: p.Plural} + + if c.Bool("cloud") { + if err := cd.HandleCdLogin(c); err != nil { + return err + } + + if err := p.backfillEncryption(); err != nil { + return err + } + } + repoRoot, err := git.Root() if err != nil { return err } - ctx, err := up.Build() + ctx, err := up.Build(c.Bool("cloud")) if err != nil { return err } + if c.Bool("cloud") { + id, name, err := cd.GetClusterId("mgmt") + if err != nil { + return err + } + + ctx.ImportCluster = lo.ToPtr(id) + ctx.CloudCluster = name + } + if err := ctx.Backfill(); err != nil { return err } - if err := ctx.Generate(); err != nil { + dir, err := ctx.Generate() + defer func() { os.RemoveAll(dir) }() + if err != nil { return err } @@ -85,6 +116,6 @@ func (p *Plural) handleUp(c *cli.Context) error { } utils.Success("Finished setting up your management cluster!\n") - utils.Highlight("Feel free to use `terrafrom` as you normally would, and leverage the gitops setup we've generated in the `apps/` subfolder\n") + 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 } diff --git a/go.mod b/go.mod index a6ecf26a..dc258807 100644 --- a/go.mod +++ b/go.mod @@ -55,8 +55,8 @@ require ( github.com/packethost/packngo v0.29.0 github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 github.com/pluralsh/cluster-api-migration v0.2.16 - github.com/pluralsh/console/go/client v1.5.0 - github.com/pluralsh/gqlclient v1.12.1 + github.com/pluralsh/console/go/client v1.12.0 + github.com/pluralsh/gqlclient v1.12.2 github.com/pluralsh/plural-operator v0.5.5 github.com/pluralsh/polly v0.1.8 github.com/pluralsh/terraform-delinker v0.0.2 diff --git a/go.sum b/go.sum index da6dd3ac..7f74ea15 100644 --- a/go.sum +++ b/go.sum @@ -1872,12 +1872,12 @@ github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= github.com/pluralsh/cluster-api-migration v0.2.16 h1:MQGrLQAhGSSpyjEDamhnJZaQ8MkxlHzR8PZxIVavLIM= github.com/pluralsh/cluster-api-migration v0.2.16/go.mod h1:24PjMsYv3vSlUiYw7BeUQ0GAtK0Jk2B1iwh35WGQLx8= -github.com/pluralsh/console/go/client v1.5.0 h1:O5pHJUXqBJWUt66MiCRlyatarNFQGy1Fy6QaC5P0avY= -github.com/pluralsh/console/go/client v1.5.0/go.mod h1:lpoWASYsM9keNePS3dpFiEisUHEfObIVlSL3tzpKn8k= +github.com/pluralsh/console/go/client v1.12.0 h1:1djVBV9GMEe1I6yKv0bWu2Qx8dnsVgGzhntGSY+KLCw= +github.com/pluralsh/console/go/client v1.12.0/go.mod h1:lpoWASYsM9keNePS3dpFiEisUHEfObIVlSL3tzpKn8k= github.com/pluralsh/controller-reconcile-helper v0.0.4 h1:1o+7qYSyoeqKFjx+WgQTxDz4Q2VMpzprJIIKShxqG0E= github.com/pluralsh/controller-reconcile-helper v0.0.4/go.mod h1:AfY0gtteD6veBjmB6jiRx/aR4yevEf6K0M13/pGan/s= -github.com/pluralsh/gqlclient v1.12.1 h1:JDOkP9jjqkPdTYdpH5hooG4F8T6FDH90XfipeXJmJFY= -github.com/pluralsh/gqlclient v1.12.1/go.mod h1:OEjN9L63x8m3A3eQBv5kVkFgiY9fp2aZ0cgOF0uII58= +github.com/pluralsh/gqlclient v1.12.2 h1:BrEFAASktf4quFw57CIaLAd+NZUTLhG08fe6tnhBQN4= +github.com/pluralsh/gqlclient v1.12.2/go.mod h1:OEjN9L63x8m3A3eQBv5kVkFgiY9fp2aZ0cgOF0uII58= github.com/pluralsh/helm-docs v1.11.3-0.20230914191425-6d14ebab8817 h1:J7SGxH6nJGdRoNtqdzhyr2VMpbl4asolul7xqqW++EA= github.com/pluralsh/helm-docs v1.11.3-0.20230914191425-6d14ebab8817/go.mod h1:rLqec59NO7YF57Rq9VlubQHMp7wcRTJhzpkcgs4lOG4= github.com/pluralsh/oauth v0.9.2 h1:tM9hBK4tCnJUeCOgX0ctxBBCS3hiCDPoxkJLODtedmQ= diff --git a/pkg/api/client.go b/pkg/api/client.go index 1fde814a..234e0417 100644 --- a/pkg/api/client.go +++ b/pkg/api/client.go @@ -99,6 +99,8 @@ type Client interface { DeleteTrust(id string) error OidcToken(provider gqlclient.ExternalOidcProvider, token, email string) (string, error) MarkSynced(repo string) error + GetConsoleInstances() ([]*gqlclient.ConsoleInstanceFragment, error) + UpdateConsoleInstance(id string, attrs gqlclient.ConsoleInstanceUpdateAttributes) error } type client struct { diff --git a/pkg/api/cloud.go b/pkg/api/cloud.go new file mode 100644 index 00000000..b5109852 --- /dev/null +++ b/pkg/api/cloud.go @@ -0,0 +1,24 @@ +package api + +import ( + "github.com/pluralsh/gqlclient" +) + +func (client *client) GetConsoleInstances() ([]*gqlclient.ConsoleInstanceFragment, error) { + res := []*gqlclient.ConsoleInstanceFragment{} + resp, err := client.pluralClient.GetConsoleInstances(client.ctx, 100) + if err != nil { + return res, err + } + + for _, node := range resp.ConsoleInstances.Edges { + res = append(res, node.Node) + } + + return res, nil +} + +func (client *client) UpdateConsoleInstance(id string, attrs gqlclient.ConsoleInstanceUpdateAttributes) error { + _, err := client.pluralClient.UpdateConsoleInstance(client.ctx, id, attrs) + return err +} diff --git a/pkg/client/plural.go b/pkg/client/plural.go index 58de041c..79078fb3 100644 --- a/pkg/client/plural.go +++ b/pkg/client/plural.go @@ -126,7 +126,7 @@ func (p *Plural) HandleInit(c *cli.Context) error { return nil } - prov, err := common.RunPreflights() + prov, err := common.RunPreflights(c) if err != nil && !c.Bool("ignore-preflights") { return err } diff --git a/pkg/common/app.go b/pkg/common/app.go index 3c2bd076..127dbb67 100644 --- a/pkg/common/app.go +++ b/pkg/common/app.go @@ -71,7 +71,7 @@ func HandleDown(_ *cli.Context) error { return fmt.Errorf("cancelled destroy") } - ctx, err := up.Build() + ctx, err := up.Build(false) if err != nil { return err } diff --git a/pkg/common/common.go b/pkg/common/common.go index 81be1224..cfdf799a 100644 --- a/pkg/common/common.go +++ b/pkg/common/common.go @@ -150,11 +150,12 @@ func postLogin(conf *config.Config, client api.Client, c *cli.Context, persist b return conf.Flush() } func Preflights(c *cli.Context) error { - _, err := RunPreflights() + _, err := RunPreflights(c) return err } -func RunPreflights() (provider.Provider, error) { +func RunPreflights(c *cli.Context) (provider.Provider, error) { + provider.SetCloudFlag(c.Bool("cloud")) prov, err := provider.GetProvider() if err != nil { return prov, err diff --git a/pkg/console/config.go b/pkg/console/config.go index 4aef3560..c9f8b625 100644 --- a/pkg/console/config.go +++ b/pkg/console/config.go @@ -6,7 +6,7 @@ import ( "os" "path/filepath" - "gopkg.in/yaml.v2" + "sigs.k8s.io/yaml" ) const ( @@ -19,14 +19,14 @@ var ( ) type VersionedConfig struct { - ApiVersion string `yaml:"apiVersion"` - Kind string `yaml:"kind"` - Spec *Config `yaml:"spec"` + ApiVersion string `json:"apiVersion"` + Kind string `json:"kind"` + Spec *Config `json:"spec"` } type Config struct { - Url string `yaml:"url"` - Token string `yaml:"token"` + Url string `json:"url"` + Token string `json:"token"` } func configFile() string { diff --git a/pkg/console/console.go b/pkg/console/console.go index bc70f3a6..e86a854b 100644 --- a/pkg/console/console.go +++ b/pkg/console/console.go @@ -90,6 +90,8 @@ func NormalizeExtUrl(uri string) string { uri = fmt.Sprintf("https://%s", uri) } + uri = strings.TrimSuffix(uri, "/") + parsed, err := url.Parse(uri) if err != nil { panic(err) @@ -106,6 +108,8 @@ func NormalizeUrl(url string) string { url = fmt.Sprintf("%s/gql", url) } + url = strings.TrimSuffix(url, "/") + return url } diff --git a/pkg/manifest/manifest.go b/pkg/manifest/manifest.go index 62549ff7..a728dcb7 100644 --- a/pkg/manifest/manifest.go +++ b/pkg/manifest/manifest.go @@ -124,15 +124,17 @@ func Read(path string) (man *Manifest, err error) { return } -func (pMan *ProjectManifest) Configure() Writer { +func (pMan *ProjectManifest) Configure(cloud bool) 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) - if err := pMan.ConfigureNetwork(); err != nil { - return nil + if !cloud { + if err := pMan.ConfigureNetwork(); err != nil { + return nil + } } if exp.IsFeatureEnabled(exp.EXP_PLURAL_CAPI) { @@ -148,11 +150,8 @@ func (pMan *ProjectManifest) ConfigureNetwork() error { } utils.Highlight("\nOk, let's get your network configuration set up now...\n") - pluralDns := utils.Confirm("Do you want to use plural's dns provider?") - modifier := " (eg something.mydomain.com)" - if pluralDns { - modifier = ", must be a subdomain under onplural.sh" - } + + modifier := ", must be a subdomain under onplural.sh" subdomain := "" input := &survey.Input{Message: fmt.Sprintf("\nWhat do you want to use as your domain%s: ", modifier)} @@ -162,15 +161,13 @@ func (pMan *ProjectManifest) ConfigureNetwork() error { return err } - if pluralDns && !strings.HasSuffix(res, pluralDomain) { + if !strings.HasSuffix(res, pluralDomain) { return fmt.Errorf("Not an onplural.sh domain") } - if pluralDns { - client := api.NewClient() - if err := client.CreateDomain(res); err != nil { - return fmt.Errorf("Domain %s is taken or your user doesn't have sufficient permissions to create domains", val) - } + client := api.NewClient() + if err := client.CreateDomain(res); err != nil { + return fmt.Errorf("Domain %s is taken or your user doesn't have sufficient permissions to create domains", val) } return nil @@ -178,7 +175,7 @@ func (pMan *ProjectManifest) ConfigureNetwork() error { return err } - pMan.Network = &NetworkConfig{Subdomain: subdomain, PluralDns: pluralDns} + pMan.Network = &NetworkConfig{Subdomain: subdomain, PluralDns: true} return nil } diff --git a/pkg/provider/aws.go b/pkg/provider/aws.go index d90a92b5..dbdac54c 100644 --- a/pkg/provider/aws.go +++ b/pkg/provider/aws.go @@ -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() + provider.writer = projectManifest.Configure(cloudFlag) provider.bucket = projectManifest.Bucket return } diff --git a/pkg/provider/azure.go b/pkg/provider/azure.go index 5bc8d14d..41f65beb 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() + prov.writer = projectManifest.Configure(cloudFlag) prov.bucket = projectManifest.Bucket return } diff --git a/pkg/provider/equinix.go b/pkg/provider/equinix.go index cd7a2de2..96a1ed4d 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() + provider.writer = projectManifest.Configure(cloudFlag) provider.bucket = projectManifest.Bucket return } diff --git a/pkg/provider/gcp.go b/pkg/provider/gcp.go index ce506ca7..2beb9e48 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() + provider.writer = projectManifest.Configure(cloudFlag) provider.bucket = projectManifest.Bucket return } diff --git a/pkg/provider/kind.go b/pkg/provider/kind.go index 00c3c55c..302b2650 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() + provider.writer = projectManifest.Configure(cloudFlag) provider.bucket = projectManifest.Bucket return } diff --git a/pkg/provider/linode.go b/pkg/provider/linode.go index 373b1cde..e92dae29 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() + provider.writer = projectManifest.Configure(cloudFlag) provider.bucket = projectManifest.Bucket return } diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index e9868d38..4bb5d07a 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -16,6 +16,8 @@ import ( "github.com/pluralsh/plural-cli/pkg/utils" ) +var cloudFlag bool + type Provider interface { Name() string Cluster() string @@ -100,6 +102,10 @@ func GetProvider() (Provider, error) { return New(provider) } +func SetCloudFlag(cloud bool) { + cloudFlag = cloud +} + func FromManifest(man *manifest.ProjectManifest) (Provider, error) { switch man.Provider { case api.ProviderGCP: diff --git a/pkg/test/mocks/Client.go b/pkg/test/mocks/Client.go index bbdd84a5..64a79f0a 100644 --- a/pkg/test/mocks/Client.go +++ b/pkg/test/mocks/Client.go @@ -616,6 +616,36 @@ func (_m *Client) GetCharts(repoId string) ([]*api.Chart, error) { return r0, r1 } +// GetConsoleInstances provides a mock function with given fields: +func (_m *Client) GetConsoleInstances() ([]*gqlclient.ConsoleInstanceFragment, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetConsoleInstances") + } + + var r0 []*gqlclient.ConsoleInstanceFragment + var r1 error + if rf, ok := ret.Get(0).(func() ([]*gqlclient.ConsoleInstanceFragment, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []*gqlclient.ConsoleInstanceFragment); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*gqlclient.ConsoleInstanceFragment) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetEabCredential provides a mock function with given fields: cluster, provider func (_m *Client) GetEabCredential(cluster string, provider string) (*api.EabCredential, error) { ret := _m.Called(cluster, provider) @@ -1814,6 +1844,24 @@ func (_m *Client) UnlockRepository(name string) error { return r0 } +// UpdateConsoleInstance provides a mock function with given fields: id, attrs +func (_m *Client) UpdateConsoleInstance(id string, attrs gqlclient.ConsoleInstanceUpdateAttributes) error { + ret := _m.Called(id, attrs) + + if len(ret) == 0 { + panic("no return value specified for UpdateConsoleInstance") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, gqlclient.ConsoleInstanceUpdateAttributes) error); ok { + r0 = rf(id, attrs) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // UpdateVersion provides a mock function with given fields: spec, tags func (_m *Client) UpdateVersion(spec *api.VersionSpec, tags []string) error { ret := _m.Called(spec, tags) diff --git a/pkg/up/context.go b/pkg/up/context.go index ee24638e..a4c29055 100644 --- a/pkg/up/context.go +++ b/pkg/up/context.go @@ -12,6 +12,7 @@ import ( "github.com/pluralsh/plural-cli/pkg/provider" "github.com/pluralsh/plural-cli/pkg/utils" "github.com/pluralsh/plural-cli/pkg/utils/git" + "github.com/samber/lo" "github.com/mitchellh/go-homedir" ) @@ -20,9 +21,13 @@ type Context struct { Provider provider.Provider Manifest *manifest.ProjectManifest Config *config.Config + Cloud bool RepoUrl string StacksIdentity string Delims *delims + ImportCluster *string + CloudCluster string + dir string } type delims struct { @@ -43,6 +48,10 @@ func (ctx *Context) changeDelims() { ctx.Delims = &delims{"[[", "]]"} } +func (ctx *Context) SetImportCluster(id string) { + ctx.ImportCluster = lo.ToPtr(id) +} + func (ctx *Context) Backfill() error { context, err := manifest.FetchContext() if err != nil { @@ -71,7 +80,7 @@ func (ctx *Context) Backfill() error { return nil } -func Build() (*Context, error) { +func Build(cloud bool) (*Context, error) { projPath, _ := filepath.Abs("workspace.yaml") project, err := manifest.ReadProject(projPath) if err != nil { @@ -88,6 +97,7 @@ func Build() (*Context, error) { Provider: prov, Config: &conf, Manifest: project, + Cloud: cloud, }, nil } diff --git a/pkg/up/deploy.go b/pkg/up/deploy.go index 012fb7c4..319906e2 100644 --- a/pkg/up/deploy.go +++ b/pkg/up/deploy.go @@ -29,6 +29,21 @@ func (ctx *Context) Deploy(commit func() error) error { return err } + if ctx.ImportCluster != nil { + prov := ctx.Provider.Name() + if err := ctx.templateFrom(ctx.path(fmt.Sprintf("templates/setup/mgmt/%s.tf", prov)), "terraform/mgmt/plural.tf"); err != nil { + return err + } + + if err := runAll([]terraformCmd{ + {dir: "./terraform/mgmt", cmd: "init", args: []string{"-upgrade"}}, + {dir: "./terraform/mgmt", cmd: "import", args: []string{"plural_cluster.mgmt", *ctx.ImportCluster}}, + {dir: "./terraform/mgmt", cmd: "apply", args: []string{"-auto-approve"}, retries: 1}, + }); err != nil { + return err + } + } + stateCmd := &terraformCmd{dir: "./terraform/mgmt"} outs, err := stateCmd.outputs() if err != nil { @@ -41,13 +56,15 @@ func (ctx *Context) Deploy(commit func() error) error { return err } - subdomain := ctx.Manifest.Network.Subdomain - if err := testDns(fmt.Sprintf("console.%s", subdomain)); err != nil { - return err - } + if !ctx.Cloud { + subdomain := ctx.Manifest.Network.Subdomain + if err := testDns(fmt.Sprintf("console.%s", subdomain)); err != nil { + return err + } - if err := ping(fmt.Sprintf("https://console.%s", subdomain)); err != nil { - return err + if err := ping(fmt.Sprintf("https://console.%s", subdomain)); err != nil { + return err + } } if err := commit(); err != nil { diff --git a/pkg/up/generate.go b/pkg/up/generate.go index 30c59011..e3b61769 100644 --- a/pkg/up/generate.go +++ b/pkg/up/generate.go @@ -3,6 +3,7 @@ package up import ( "fmt" "os" + "path/filepath" "github.com/pluralsh/plural-cli/pkg/utils" "github.com/pluralsh/plural-cli/pkg/utils/git" @@ -12,6 +13,8 @@ type templatePair struct { from string to string overwrite bool + cloud bool + cloudless bool } func (ctx *Context) Cleanup() { @@ -19,24 +22,29 @@ func (ctx *Context) Cleanup() { _ = os.RemoveAll("./bootstrap") } -func (ctx *Context) Generate() error { - if !utils.Exists("./bootstrap") { - if err := git.BranchedSubmodule("https://github.com/pluralsh/bootstrap.git", "main"); err != nil { - return err - } +func (ctx *Context) Generate() (dir string, err error) { + dir, err = os.MkdirTemp("", "sampledir") + ctx.dir = dir + if err != nil { + return + } + + if err = git.PathClone("https://github.com/pluralsh/bootstrap.git", "more-up-improvements", dir); err != nil { + return } prov := ctx.Provider.Name() tpls := []templatePair{ - {from: "./bootstrap/charts/runtime/values.yaml.tpl", to: "./helm-values/runtime.yaml", overwrite: true}, - {from: "./bootstrap/helm/certmanager.yaml", to: "./helm-values/certmanager.yaml", overwrite: true}, - {from: "./bootstrap/helm/flux.yaml", to: "./helm-values/flux.yaml", overwrite: true}, - {from: fmt.Sprintf("./bootstrap/templates/providers/bootstrap/%s.tf", prov), to: "terraform/mgmt/provider.tf"}, - {from: fmt.Sprintf("./bootstrap/templates/setup/providers/%s.tf", prov), to: "terraform/mgmt/mgmt.tf"}, - {from: "./bootstrap/templates/setup/console.tf", to: "terraform/mgmt/console.tf"}, - {from: fmt.Sprintf("./bootstrap/templates/providers/apps/%s.tf", prov), to: "terraform/apps/provider.tf"}, - {from: "./bootstrap/templates/setup/cd.tf", to: "terraform/apps/cd.tf"}, - {from: "./bootstrap/README.md", to: "README.md", overwrite: true}, + {from: ctx.path("charts/runtime/values.yaml.tpl"), to: "./helm-values/runtime.yaml", overwrite: true}, + {from: ctx.path("helm/certmanager.yaml"), to: "./helm-values/certmanager.yaml", overwrite: true}, + {from: ctx.path("helm/flux.yaml"), to: "./helm-values/flux.yaml", overwrite: true}, + {from: ctx.path(fmt.Sprintf("templates/providers/bootstrap/%s.tf", prov)), to: "terraform/mgmt/provider.tf"}, + {from: ctx.path(fmt.Sprintf("templates/setup/providers/%s.tf", prov)), to: "terraform/mgmt/mgmt.tf"}, + {from: ctx.path("templates/setup/console.tf"), to: "terraform/mgmt/console.tf", cloudless: true}, + {from: ctx.path(fmt.Sprintf("templates/providers/apps/%s.tf", prov)), to: "terraform/apps/provider.tf", cloudless: true}, + {from: ctx.path("templates/providers/apps/cloud.tf"), to: "terraform/apps/provider.tf", cloud: true}, + {from: ctx.path("templates/setup/cd.tf"), to: "terraform/apps/cd.tf"}, + {from: ctx.path("README.md"), to: "README.md", overwrite: true}, } for _, tpl := range tpls { @@ -45,17 +53,24 @@ func (ctx *Context) Generate() error { continue } - if err := ctx.templateFrom(tpl.from, tpl.to); err != nil { - return err + if tpl.cloudless && ctx.Cloud { + continue + } + + if tpl.cloud && !ctx.Cloud { + continue + } + + if err = ctx.templateFrom(tpl.from, tpl.to); err != nil { + return } } copies := []templatePair{ - {from: "./bootstrap/terraform/modules/clusters", to: "terraform/modules/clusters"}, - {from: fmt.Sprintf("./bootstrap/terraform/clouds/%s", prov), to: "terraform/mgmt/cluster"}, - {from: "./bootstrap/apps/repositories", to: "apps/repositories"}, - {from: "./bootstrap/apps/services", to: "apps/services"}, - {from: "./bootstrap/templates", to: "templates"}, + {from: ctx.path("terraform/modules/clusters"), to: "terraform/modules/clusters"}, + {from: ctx.path(fmt.Sprintf("terraform/clouds/%s", prov)), to: "terraform/mgmt/cluster"}, + {from: ctx.path("setup"), to: "setup"}, + {from: ctx.path("templates"), to: "templates"}, } for _, copy := range copies { @@ -63,30 +78,37 @@ func (ctx *Context) Generate() error { continue } - if err := utils.CopyDir(copy.from, copy.to); err != nil { - return err + if err = utils.CopyDir(copy.from, copy.to); err != nil { + return + } + } + + if ctx.Cloud { + toRemove := []string{"setup/console.yaml", "setup/flux.yaml"} + for _, f := range toRemove { + os.Remove(f) } } ctx.changeDelims() overwrites := []templatePair{ - {from: "apps/services/setup.yaml", to: "apps/services/setup.yaml"}, - {from: "apps/services/pr-automation/cluster-creator.yaml", to: "apps/services/pr-automation/cluster-creator.yaml"}, + {from: "setup/setup.yaml", to: "setup/setup.yaml"}, + {from: "setup/pr-automation/cluster-creator.yaml", to: "setup/pr-automation/cluster-creator.yaml"}, } for _, tpl := range overwrites { - if err := ctx.templateFrom(tpl.from, tpl.to); err != nil { - return err + if err = ctx.templateFrom(tpl.from, tpl.to); err != nil { + return } } - return nil + return } func (ctx *Context) afterSetup() error { prov := ctx.Provider.Name() overwrites := []templatePair{ - {from: fmt.Sprintf("./bootstrap/templates/setup/stacks/%s.yaml", prov), to: "apps/services/stacks/serviceaccount.yaml"}, + {from: ctx.path(fmt.Sprintf("templates/setup/stacks/%s.yaml", prov)), to: "setup/stacks/serviceaccount.yaml"}, } ctx.Delims = nil @@ -98,3 +120,7 @@ func (ctx *Context) afterSetup() error { return nil } + +func (ctx *Context) path(p string) string { + return filepath.Join(ctx.dir, p) +} diff --git a/pkg/up/prune.go b/pkg/up/prune.go index 1b2e873a..0b0616f5 100644 --- a/pkg/up/prune.go +++ b/pkg/up/prune.go @@ -9,6 +9,10 @@ import ( ) func (ctx *Context) Prune() error { + if ctx.Cloud { + return nil + } + utils.Highlight("\nCleaning up unneeded resources...\n\n") repoRoot, err := git.Root() if err != nil { diff --git a/pkg/up/template.go b/pkg/up/template.go index 29cbfdda..0beb2683 100644 --- a/pkg/up/template.go +++ b/pkg/up/template.go @@ -38,8 +38,6 @@ func (ctx *Context) template(tmplate string) (string, error) { values := map[string]interface{}{ "Cluster": cluster, "Provider": provider, - "Subdomain": ctx.Manifest.Network.Subdomain, - "Network": ctx.Manifest.Network, "Bucket": ctx.Provider.Bucket(), "Project": ctx.Provider.Project(), "Region": ctx.Provider.Region(), @@ -49,6 +47,13 @@ func (ctx *Context) template(tmplate string) (string, error) { "Identifier": ctx.identifier(), "Acme": eabCredential, "StacksIdentity": ctx.StacksIdentity, + "RequireDB": !ctx.Cloud, + "CloudCluster": ctx.CloudCluster, + "Cloud": ctx.Cloud, + } + if ctx.Manifest.Network != nil { + values["Subdomain"] = ctx.Manifest.Network.Subdomain + values["Network"] = ctx.Manifest.Network } tpl := template.New("tpl") diff --git a/pkg/utils/git/repo.go b/pkg/utils/git/repo.go index 980fa6e9..316ac6dc 100644 --- a/pkg/utils/git/repo.go +++ b/pkg/utils/git/repo.go @@ -39,6 +39,11 @@ func BranchedSubmodule(url, branch string) error { return err } +func PathClone(url, branch, path string) error { + _, err := GitRaw("clone", url, "-b", branch, path) + return err +} + func Rm(path string) error { _, err := GitRaw("rm", path) return err