diff --git a/cmd/plural/cd.go b/cmd/plural/cd.go index 9de1acbc5..199829508 100644 --- a/cmd/plural/cd.go +++ b/cmd/plural/cd.go @@ -44,6 +44,17 @@ func (p *Plural) cdCommands() []cli.Command { Action: p.handleInstallControlPlane, Usage: "sets up the plural console in an existing k8s cluster", }, + { + Name: "control-plane-values", + Action: p.handlePrintControlPlaneValues, + Usage: "dumps a values file for installing the plural console", + Flags: []cli.Flag{ + cli.StringFlag{Name: "domain", Usage: "The plural domain to use for this console", Required: true}, + cli.StringFlag{Name: "dsn", Usage: "The Postgres DSN to use for database connections", Required: true}, + cli.StringFlag{Name: "name", Usage: "The name given to the cluster", Required: true}, + cli.StringFlag{Name: "file", Usage: "The file to dump values to", Required: true}, + }, + }, { Name: "uninstall", Action: p.handleUninstallOperator, @@ -166,6 +177,16 @@ func (p *Plural) handleInstallControlPlane(_ *cli.Context) error { return nil } +func (p *Plural) handlePrintControlPlaneValues(c *cli.Context) error { + conf := config.Read() + vals, err := cd.ControlPlaneValues(conf, c.String("domain"), c.String("dsn"), c.String("name")) + if err != nil { + return err + } + + return os.WriteFile(c.String("file"), []byte(vals), 0644) +} + func (p *Plural) handleEject(c *cli.Context) (err error) { if !c.Args().Present() { return fmt.Errorf("clusterid cannot be empty") diff --git a/cmd/plural/crypto.go b/cmd/plural/crypto.go index 3193b18c6..feb0046fd 100644 --- a/cmd/plural/crypto.go +++ b/cmd/plural/crypto.go @@ -41,6 +41,7 @@ context.yaml filter=plural-crypt diff=plural-crypt workspace.yaml filter=plural-crypt diff=plural-crypt context.yaml* filter=plural-crypt diff=plural-crypt workspace.yaml* filter=plural-crypt diff=plural-crypt +helm-values/*.yaml filter=plural-crypt diff=plural-crypt .gitattributes !filter !diff ` diff --git a/cmd/plural/init.go b/cmd/plural/init.go index 04b8299e0..47ad4f310 100644 --- a/cmd/plural/init.go +++ b/cmd/plural/init.go @@ -29,6 +29,11 @@ func (p *Plural) handleInit(c *cli.Context) error { gitCreated := false repo := "" + if utils.Exists("./workspace.yaml") { + utils.Highlight("Found workspace.yaml, skipping init as this repo has already been initialized...\n") + return nil + } + git, err := wkspace.Preflight() if err != nil && git { return err diff --git a/cmd/plural/plural.go b/cmd/plural/plural.go index d832e1235..be87eb7a2 100644 --- a/cmd/plural/plural.go +++ b/cmd/plural/plural.go @@ -105,6 +105,53 @@ func (p *Plural) getCommands() []cli.Command { Usage: "Gets cli version info", Action: versionInfo, }, + { + Name: "up", + Usage: "sets up your repository and an initial management cluster", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "endpoint", + Usage: "the endpoint for the plural installation you're working with", + }, + cli.StringFlag{ + Name: "service-account", + Usage: "email for the service account you'd like to use for this workspace", + }, + cli.BoolFlag{ + Name: "ignore-preflights", + Usage: "whether to ignore preflight check failures prior to init", + }, + cli.StringFlag{ + Name: "commit", + Usage: "commits your changes with this message", + }, + }, + Action: latestVersion(p.handleUp), + }, + { + Name: "down", + Usage: "destroys your management cluster and any apps installed on it", + Action: latestVersion(p.handleDown), + }, + { + Name: "init", + Usage: "initializes plural within a git repo", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "endpoint", + Usage: "the endpoint for the plural installation you're working with", + }, + cli.StringFlag{ + Name: "service-account", + Usage: "email for the service account you'd like to use for this workspace", + }, + cli.BoolFlag{ + Name: "ignore-preflights", + Usage: "whether to ignore preflight check failures prior to init", + }, + }, + Action: tracked(latestVersion(p.handleInit), "cli.init"), + }, { Name: "build", Aliases: []string{"bld"}, @@ -272,25 +319,6 @@ func (p *Plural) getCommands() []cli.Command { Usage: "Handles authentication to the plural api", Subcommands: p.authCommands(), }, - { - Name: "init", - Usage: "initializes plural within a git repo", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "endpoint", - Usage: "the endpoint for the plural installation you're working with", - }, - cli.StringFlag{ - Name: "service-account", - Usage: "email for the service account you'd like to use for this workspace", - }, - cli.BoolFlag{ - Name: "ignore-preflights", - Usage: "whether to ignore preflight check failures prior to init", - }, - }, - Action: tracked(latestVersion(p.handleInit), "cli.init"), - }, { Name: "preflights", Usage: "runs provider preflight checks", @@ -443,17 +471,11 @@ func (p *Plural) getCommands() []cli.Command { Subcommands: p.opsCommands(), Category: "Debugging", }, - { - Name: "utils", - Usage: "useful plural utilities", - Subcommands: utilsCommands(), - Category: "Miscellaneous", - }, { Name: "vpn", Usage: "interacting with the plural vpn", Subcommands: p.vpnCommands(), - Category: "Miscellaneous", + Category: "Workspace", }, { Name: "ai", @@ -501,12 +523,6 @@ func (p *Plural) getCommands() []cli.Command { Action: latestVersion(diffed), Category: "Workspace", }, - { - Name: "from-grafana", - Usage: "imports a grafana dashboard to a plural crd", - Action: latestVersion(formatDashboard), - Category: "Publishing", - }, { Name: "bootstrap", Usage: "Commands for bootstrapping cluster", diff --git a/cmd/plural/up.go b/cmd/plural/up.go new file mode 100644 index 000000000..4bc7c2789 --- /dev/null +++ b/cmd/plural/up.go @@ -0,0 +1,67 @@ +package plural + +import ( + "fmt" + + "github.com/urfave/cli" + + "github.com/pluralsh/plural-cli/pkg/up" + "github.com/pluralsh/plural-cli/pkg/utils" + "github.com/pluralsh/plural-cli/pkg/utils/git" +) + +const ( + affirmUp = "Are you ready to set up your initial management cluster? You can check the generated terraform/helm to confirm everything looks good first" + affirmDown = "Are you ready to destroy your plural infrastructure? This will destroy all k8s clusters and any data stored within" +) + +func (p *Plural) handleUp(c *cli.Context) error { + if err := p.handleInit(c); err != nil { + return err + } + p.InitPluralClient() + + repoRoot, err := git.Root() + if err != nil { + return err + } + + ctx, err := up.Build() + if err != nil { + return err + } + + if err := ctx.Generate(); err != nil { + return err + } + + if !affirm(affirmUp, "PLURAL_UP_AFFIRM_DEPLOY") { + return fmt.Errorf("cancelled deploy") + } + + if err := ctx.Deploy(); err != nil { + return err + } + + utils.Highlight("\n==> Commit and push your configuration\n\n") + + if commit := commitMsg(c); commit != "" { + utils.Highlight("Pushing upstream...\n") + return git.Sync(repoRoot, commit, c.Bool("force")) + } + + return nil +} + +func (p *Plural) handleDown(c *cli.Context) error { + if !affirm(affirmDown, "PLURAL_DOWN_AFFIRM_DESTROY") { + return fmt.Errorf("cancelled destroy") + } + + ctx, err := up.Build() + if err != nil { + return err + } + + return ctx.Destroy() +} diff --git a/cmd/plural/utils.go b/cmd/plural/utils.go deleted file mode 100644 index 991173848..000000000 --- a/cmd/plural/utils.go +++ /dev/null @@ -1,127 +0,0 @@ -package plural - -import ( - "bufio" - "fmt" - "os" - "path/filepath" - "regexp" - "strings" - - "github.com/coreos/go-semver/semver" - "github.com/pluralsh/plural-cli/pkg/utils" - "github.com/pluralsh/plural-cli/pkg/utils/pathing" - "github.com/thoas/go-funk" - "github.com/urfave/cli" - "sigs.k8s.io/yaml" -) - -func utilsCommands() []cli.Command { - return []cli.Command{ - { - Name: "image-bump", - ArgsUsage: "CHART", - Usage: "Bumps a chart's image tag", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "path", - Usage: "path to tag in helm values file", - }, - cli.StringFlag{ - Name: "tag", - Usage: "the image tag to set to", - }, - }, - Action: latestVersion(handleImageBump), - }, - } -} - -func handleImageBump(c *cli.Context) error { - chartPath := c.Args().Get(0) - path, tag := c.String("path"), c.String("tag") - - chartPath, _ = filepath.Abs(chartPath) - - chart := make(map[string]interface{}) - vals := make(map[string]interface{}) - - if err := readHelmYaml(pathing.SanitizeFilepath(filepath.Join(chartPath, "Chart.yaml")), &chart); err != nil { - return err - } - - if err := readHelmYaml(pathing.SanitizeFilepath(filepath.Join(chartPath, "values.yaml")), &vals); err != nil { - return err - } - - currentTag := funk.Get(vals, path) - if currentTag == tag { - utils.Highlight("No change in version tag\n") - return nil - } - - currentVersion := (chart["version"]).(string) - sv, err := semver.NewVersion(currentVersion) - if err != nil { - return err - } - - if err := replaceVals(pathing.SanitizeFilepath(filepath.Join(chartPath, "values.yaml")), tag); err != nil { - return err - } - - sv.BumpPatch() - chart["version"] = sv.String() - - return writeHelmYaml(pathing.SanitizeFilepath(filepath.Join(chartPath, "Chart.yaml")), chart) -} - -const replPattern = `(?s).*## PLRL-REPLACE\[(.*)\].*` - -func replaceVals(path string, val string) error { - file, err := os.Open(path) - if err != nil { - return err - } - defer file.Close() - - result := make([]string, 0) - scanner := bufio.NewScanner(file) - // optionally, resize scanner's capacity for lines over 64K, see next example - re := regexp.MustCompile(replPattern) - for scanner.Scan() { - line := scanner.Text() - matches := re.FindStringSubmatch(line) - if len(matches) > 0 { - line = fmt.Sprintf(matches[1]+" ## PLRL-REPLACE[%s]", val, matches[1]) - } - - result = append(result, line) - } - - if err := scanner.Err(); err != nil { - return err - } - - content := strings.Join(result, "\n") - - return os.WriteFile(path, []byte(content), 0644) -} - -func readHelmYaml(path string, result *map[string]interface{}) error { - content, err := os.ReadFile(path) - if err != nil { - return err - } - - return yaml.Unmarshal(content, result) -} - -func writeHelmYaml(path string, vals map[string]interface{}) error { - io, err := yaml.Marshal(&vals) - if err != nil { - return err - } - - return os.WriteFile(path, io, 0644) -} diff --git a/pkg/cd/control_plane_install.go b/pkg/cd/control_plane_install.go index bbe9c7dbf..13a458f8c 100644 --- a/pkg/cd/control_plane_install.go +++ b/pkg/cd/control_plane_install.go @@ -20,9 +20,97 @@ var ( ) const ( - templateUrl = "https://raw.githubusercontent.com/pluralsh/console/cd-scaffolding/charts/console/values.yaml.liquid" + templateUrl = "https://raw.githubusercontent.com/pluralsh/console/fix-migration-job/templates/values.yaml.liquid" ) +func ControlPlaneValues(conf config.Config, domain, dsn, name string) (string, error) { + consoleDns := fmt.Sprintf("console.%s", domain) + kasDns := fmt.Sprintf("kas.%s", domain) + randoms := map[string]string{} + for _, key := range []string{"jwt", "erlang", "adminPassword", "kasApi", "kasPrivateApi", "kasRedis"} { + rand, err := crypto.RandStr(32) + if err != nil { + return "", err + } + randoms[key] = rand + } + + client := api.FromConfig(&conf) + me, err := client.Me() + if err != nil { + return "", fmt.Errorf("you must run `plural login` before installing") + } + + configuration := map[string]string{ + "consoleDns": consoleDns, + "kasDns": kasDns, + "aesKey": utils.GenAESKey(), + "adminName": me.Email, + "adminEmail": me.Email, + "clusterName": name, + "pluralToken": conf.Token, + "postgresUrl": dsn, + "clusterIssuer": "plural", + } + for k, v := range randoms { + configuration[k] = v + } + + cryptos, err := cryptoVals() + if err != nil { + return "", err + } + + for k, v := range cryptos { + configuration[k] = v + } + + clientId, clientSecret, err := ensureInstalledAndOidc(client, consoleDns) + if err != nil { + return "", err + } + configuration["pluralClientId"] = clientId + configuration["pluralClientSecret"] = clientSecret + + tpl, err := fetchTemplate() + if err != nil { + return "", err + } + + bindings := map[string]interface{}{ + "configuration": configuration, + } + + res, err := liquidEngine.ParseAndRender(tpl, bindings) + return string(res), err +} + +func cryptoVals() (map[string]string, error) { + res := make(map[string]string) + keyFile, err := config.PluralDir("key") + if err != nil { + return res, err + } + + aes, err := utils.ReadFile(keyFile) + if err != nil { + return res, err + } + res["key"] = aes + + identityFile, err := config.PluralDir("identity") + if err != nil { + return res, nil + } + + identity, err := utils.ReadFile(identityFile) + if err != nil { + return res, nil + } + res["identity"] = identity + return res, nil +} + func CreateControlPlane(conf config.Config) (string, error) { client := api.FromConfig(&conf) me, err := client.Me() diff --git a/pkg/provider/aws.go b/pkg/provider/aws.go index 7ff94b1bd..d90a92b5f 100644 --- a/pkg/provider/aws.go +++ b/pkg/provider/aws.go @@ -237,6 +237,10 @@ func getAwsConfig(ctx context.Context) (aws.Config, error) { return cfg, plrlErrors.ErrorWrap(err, "Failed to initialize aws client: ") } +func (aws *AWSProvider) CreateBucket() error { + return aws.mkBucket(aws.bucket) +} + func (aws *AWSProvider) CreateBackend(prefix string, version string, ctx map[string]interface{}) (string, error) { if err := aws.mkBucket(aws.bucket); err != nil { return "", plrlErrors.ErrorWrap(err, fmt.Sprintf("Failed to create terraform state bucket %s", aws.bucket)) diff --git a/pkg/provider/azure.go b/pkg/provider/azure.go index e160015c9..5bc8d14da 100644 --- a/pkg/provider/azure.go +++ b/pkg/provider/azure.go @@ -201,13 +201,21 @@ func AzureFromManifest(man *manifest.ProjectManifest, clientSet *ClientSet) (*Az return &AzureProvider{man.Cluster, man.Project, man.Bucket, man.Region, man.Context, nil, clients}, nil } -func (az *AzureProvider) CreateBackend(prefix string, version string, ctx map[string]interface{}) (string, error) { +func (az *AzureProvider) CreateBucket() error { if err := az.CreateResourceGroup(az.Project()); err != nil { - return "", pluralerr.ErrorWrap(err, fmt.Sprintf("Failed to create terraform state resource group %s", az.Project())) + return pluralerr.ErrorWrap(err, fmt.Sprintf("Failed to create terraform state resource group %s", az.Project())) + } + + if err := az.createContainer(az.bucket); err != nil { + return pluralerr.ErrorWrap(err, fmt.Sprintf("Failed to create terraform state bucket %s", az.bucket)) } - if err := az.CreateBucket(az.bucket); err != nil { - return "", pluralerr.ErrorWrap(err, fmt.Sprintf("Failed to create terraform state bucket %s", az.bucket)) + return nil +} + +func (az *AzureProvider) CreateBackend(prefix string, version string, ctx map[string]interface{}) (string, error) { + if err := az.CreateBucket(); err != nil { + return "", err } ctx["Region"] = az.Region() @@ -230,7 +238,7 @@ func (az *AzureProvider) CreateBackend(prefix string, version string, ctx map[st return template.RenderString(scaffold, ctx) } -func (az *AzureProvider) CreateBucket(bucket string) (err error) { +func (az *AzureProvider) createContainer(bucket string) (err error) { acc, err := az.upsertStorageAccount(utils.ToString(az.Context()["StorageAccount"])) if err != nil { return diff --git a/pkg/provider/equinix.go b/pkg/provider/equinix.go index a4a9edd40..cd7a2de23 100644 --- a/pkg/provider/equinix.go +++ b/pkg/provider/equinix.go @@ -117,6 +117,10 @@ func equinixFromManifest(man *manifest.ProjectManifest) (*EQUINIXProvider, error return &EQUINIXProvider{man.Cluster, man.Project, man.Bucket, man.Region, man.Context, nil}, nil } +func (equinix *EQUINIXProvider) CreateBucket() error { + return nil +} + func (equinix *EQUINIXProvider) CreateBackend(prefix string, version string, ctx map[string]interface{}) (string, error) { ctx["Region"] = equinix.Region() diff --git a/pkg/provider/gcp.go b/pkg/provider/gcp.go index 3f2e66b67..ce506ca75 100644 --- a/pkg/provider/gcp.go +++ b/pkg/provider/gcp.go @@ -248,9 +248,14 @@ func (gcp *GCPProvider) Flush() error { return gcp.writer() } +func (gcp *GCPProvider) CreateBucket() error { + err := gcp.mkBucket(gcp.bucket) + return utilerr.ErrorWrap(err, fmt.Sprintf("Failed to create terraform state bucket %s", gcp.Bucket())) +} + func (gcp *GCPProvider) CreateBackend(prefix string, version string, ctx map[string]interface{}) (string, error) { - if err := gcp.mkBucket(gcp.bucket); err != nil { - return "", utilerr.ErrorWrap(err, fmt.Sprintf("Failed to create terraform state bucket %s", gcp.Bucket())) + if err := gcp.CreateBucket(); err != nil { + return "", err } ctx["Project"] = gcp.Project() diff --git a/pkg/provider/kind.go b/pkg/provider/kind.go index cb3c24477..00c3c55cd 100644 --- a/pkg/provider/kind.go +++ b/pkg/provider/kind.go @@ -71,6 +71,8 @@ func kindFromManifest(man *manifest.ProjectManifest) (*KINDProvider, error) { return &KINDProvider{man.Cluster, man.Project, man.Bucket, man.Region, man.Context, nil}, nil } +func (kind *KINDProvider) CreateBucket() error { return nil } + func (kind *KINDProvider) CreateBackend(prefix string, version string, ctx map[string]interface{}) (string, error) { ctx["Region"] = kind.Region() diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 507b04bc6..fc407c1d0 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -25,6 +25,7 @@ type Provider interface { KubeConfig() error KubeContext() string CreateBackend(prefix string, version string, ctx map[string]interface{}) (string, error) + CreateBucket() error Context() map[string]interface{} Decommision(node *v1.Node) error Preflights() []*Preflight diff --git a/pkg/provider/test.go b/pkg/provider/test.go index 9519367a5..2b4a578a2 100644 --- a/pkg/provider/test.go +++ b/pkg/provider/test.go @@ -44,6 +44,8 @@ func (t TestProvider) KubeContext() string { return t.Clust } +func (t TestProvider) CreateBucket() error { return nil } + func (t TestProvider) CreateBackend(prefix string, version string, ctx map[string]interface{}) (string, error) { return "test", nil } diff --git a/pkg/up/context.go b/pkg/up/context.go new file mode 100644 index 000000000..484b74cd7 --- /dev/null +++ b/pkg/up/context.go @@ -0,0 +1,35 @@ +package up + +import ( + "path/filepath" + + "github.com/pluralsh/plural-cli/pkg/config" + "github.com/pluralsh/plural-cli/pkg/manifest" + "github.com/pluralsh/plural-cli/pkg/provider" +) + +type Context struct { + Provider provider.Provider + Manifest *manifest.ProjectManifest + Config *config.Config +} + +func Build() (*Context, error) { + projPath, _ := filepath.Abs("workspace.yaml") + project, err := manifest.ReadProject(projPath) + if err != nil { + return nil, err + } + + prov, err := provider.FromManifest(project) + if err != nil { + return nil, err + } + + conf := config.Read() + return &Context{ + Provider: prov, + Config: &conf, + Manifest: project, + }, nil +} diff --git a/pkg/up/deploy.go b/pkg/up/deploy.go new file mode 100644 index 000000000..32e24a4d0 --- /dev/null +++ b/pkg/up/deploy.go @@ -0,0 +1,71 @@ +package up + +import ( + "fmt" + "os" + "os/exec" + "time" + + "github.com/pluralsh/plural-cli/pkg/utils" +) + +type terraformCmd struct { + dir string + cmd string + args []string + retries int +} + +func (ctx *Context) Deploy() error { + if err := ctx.Provider.CreateBucket(); err != nil { + return err + } + + if err := runAll([]terraformCmd{ + {dir: "./clusters", cmd: "init", args: []string{"-upgrade"}}, + {dir: "./clusters", cmd: "apply", args: []string{"-auto-approve"}, retries: 1}, + }); err != nil { + return err + } + + return ping(fmt.Sprintf("https://console.%s", ctx.Manifest.Network.Subdomain)) +} + +func (ctx *Context) Destroy() error { + return runAll([]terraformCmd{ + {dir: "./clusters", cmd: "init", args: []string{"-upgrade"}}, + {dir: "./clusters", cmd: "destroy", args: []string{"-auto-approve"}, retries: 1}, + }) +} + +func runAll(cmds []terraformCmd) error { + for _, cmd := range cmds { + if err := cmd.run(); err != nil { + return err + } + } + + return nil +} + +func (tf *terraformCmd) run() (err error) { + for tf.retries >= 0 { + args := append([]string{tf.cmd}, tf.args...) + cmd := exec.Command("terraform", args...) + cmd.Dir = tf.dir + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + if err == nil { + return + } + + tf.retries -= 1 + if tf.retries >= 0 { + utils.Warn("terraform cmd failed, retrying") + time.Sleep(10 * time.Second) + } + } + + return +} diff --git a/pkg/up/generate.go b/pkg/up/generate.go new file mode 100644 index 000000000..8a8fffe5f --- /dev/null +++ b/pkg/up/generate.go @@ -0,0 +1,45 @@ +package up + +import ( + "fmt" + + "github.com/pluralsh/plural-cli/pkg/utils" + "github.com/pluralsh/plural-cli/pkg/utils/git" +) + +type templatePair struct { + from string + to string + overwrite bool +} + +func (ctx *Context) Generate() error { + if !utils.Exists("./bootstrap") { + if err := git.Submodule("https://github.com/pluralsh/bootstrap.git"); err != nil { + return err + } + } + + prov := ctx.Provider.Name() + tpls := []templatePair{ + {from: "./bootstrap/charts/runtime/values.yaml.tpl", to: "./helm-values/runtime.yaml", overwrite: true}, + {from: fmt.Sprintf("./bootstrap/templates/providers/bootstrap/%s.tf", prov), to: "clusters/provider.tf"}, + {from: fmt.Sprintf("./bootstrap/templates/setup/providers/%s.tf", prov), to: "clusters/mgmt.tf"}, + {from: "./bootstrap/templates/setup/console.tf", to: "clusters/console.tf"}, + {from: fmt.Sprintf("./bootstrap/templates/providers/apps/%s.tf", prov), to: "apps/provider.tf"}, + {from: "./bootstrap/README.md", to: "README.md", overwrite: true}, + } + + for _, tpl := range tpls { + if utils.Exists(tpl.to) && !tpl.overwrite { + fmt.Printf("%s already exists, skipping for now...\n", tpl.to) + continue + } + + if err := ctx.templateFrom(tpl.from, tpl.to); err != nil { + return err + } + } + + return nil +} diff --git a/pkg/up/ping.go b/pkg/up/ping.go new file mode 100644 index 000000000..88a660383 --- /dev/null +++ b/pkg/up/ping.go @@ -0,0 +1,36 @@ +package up + +import ( + "context" + "fmt" + "net/http" + "time" + + "github.com/pluralsh/plural-cli/pkg/utils" +) + +func ping(url string) error { + done := make(chan bool) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + go func() { + for { + fmt.Printf("Pinging %s...\n", url) + resp, err := http.Get(url) + if err == nil && resp.StatusCode == 200 { + utils.Success("Found status code 200, console up!\n") + done <- true + return + } + time.Sleep(10 * time.Second) + } + }() + + select { + case <-done: + return nil + case <-ctx.Done(): + return fmt.Errorf("Console failed to become ready after 5 minutes, you might want to inspect the resources in the plrl-console namespace") + } +} diff --git a/pkg/up/template.go b/pkg/up/template.go new file mode 100644 index 000000000..39e0ee585 --- /dev/null +++ b/pkg/up/template.go @@ -0,0 +1,60 @@ +package up + +import ( + "bytes" + "time" + + "text/template" + + "github.com/pluralsh/plural-cli/pkg/api" + "github.com/pluralsh/plural-cli/pkg/utils" + "github.com/pluralsh/polly/retry" +) + +func (ctx *Context) templateFrom(file, to string) error { + buf, err := utils.ReadFile(file) + if err != nil { + return err + } + + res, err := ctx.template(buf) + if err != nil { + return err + } + + return utils.WriteFile(to, []byte(res)) +} + +func (ctx *Context) template(tmplate string) (string, error) { + cluster, provider := ctx.Provider.Cluster(), ctx.Provider.Name() + client := api.NewClient() + retrier := retry.NewConstant(15*time.Millisecond, 3) + eabCredential, err := retry.Retry(retrier, func() (*api.EabCredential, error) { + return client.GetEabCredential(cluster, provider) + }) + if err != nil { + return "", err + } + + 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(), + "Context": ctx.Provider.Context(), + "Config": ctx.Config, + "Acme": eabCredential, + } + + tpl, err := template.New("gotpl").Parse(tmplate) + if err != nil { + return "", err + } + + var buf bytes.Buffer + err = tpl.Execute(&buf, values) + return buf.String(), err +} diff --git a/pkg/utils/git/repo.go b/pkg/utils/git/repo.go index 33939748e..dfc11119c 100644 --- a/pkg/utils/git/repo.go +++ b/pkg/utils/git/repo.go @@ -24,6 +24,11 @@ func CurrentBranch() (string, error) { return GitRaw("rev-parse", "--abbrev-ref", "HEAD") } +func Submodule(url string) error { + _, err := GitRaw("submodule", "add", url) + return err +} + func HasUpstreamChanges() (bool, string, error) { headRef, err := GitRaw("rev-parse", "--symbolic-full-name", "HEAD") if err != nil {