From c3302bb820bea11241ce2edf091d31d7ae8662c3 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 4 Dec 2024 15:01:31 +0100 Subject: [PATCH] remove unused code --- cmd/command/clusters/clusters.go | 207 -------------- cmd/command/plural/plural.go | 21 -- cmd/command/workspace/workspace.go | 12 - pkg/bundle/installer.go | 117 -------- pkg/client/build.go | 149 ---------- pkg/common/app.go | 23 -- pkg/common/apply.go | 33 --- pkg/common/common.go | 51 ---- pkg/common/diff.go | 75 ----- pkg/common/scaffold.go | 12 - pkg/common/validation.go | 93 ------- pkg/common/version.go | 10 - pkg/helm/helm.go | 19 -- pkg/logs/api.go | 33 --- pkg/output/terraform.go | 32 --- pkg/scaffold/application.go | 66 ----- pkg/scaffold/application_test.go | 74 ----- pkg/scaffold/constants.go | 116 -------- pkg/scaffold/crd.go | 52 ---- pkg/scaffold/creator.go | 102 ------- pkg/scaffold/default.go | 59 ---- pkg/scaffold/helm.go | 431 ----------------------------- pkg/scaffold/helm_test.go | 214 -------------- pkg/scaffold/readme.go | 186 ------------- pkg/scaffold/scaffold.go | 166 ----------- pkg/scaffold/secrets.go | 20 -- pkg/scaffold/template/template.go | 40 --- pkg/scaffold/terraform.go | 283 ------------------- pkg/wkspace/actions.go | 172 ------------ pkg/wkspace/builder.go | 266 +----------------- pkg/wkspace/builder_test.go | 59 ---- pkg/wkspace/graph.go | 113 -------- pkg/wkspace/manifest.go | 82 ------ pkg/wkspace/notes.go | 1 - pkg/wkspace/validator.go | 16 -- 35 files changed, 3 insertions(+), 3402 deletions(-) delete mode 100644 cmd/command/clusters/clusters.go delete mode 100644 pkg/bundle/installer.go delete mode 100644 pkg/client/build.go delete mode 100644 pkg/common/apply.go delete mode 100644 pkg/common/diff.go delete mode 100644 pkg/common/scaffold.go delete mode 100644 pkg/logs/api.go delete mode 100644 pkg/output/terraform.go delete mode 100644 pkg/scaffold/application.go delete mode 100644 pkg/scaffold/application_test.go delete mode 100644 pkg/scaffold/constants.go delete mode 100644 pkg/scaffold/crd.go delete mode 100644 pkg/scaffold/creator.go delete mode 100644 pkg/scaffold/default.go delete mode 100644 pkg/scaffold/helm.go delete mode 100644 pkg/scaffold/helm_test.go delete mode 100644 pkg/scaffold/readme.go delete mode 100644 pkg/scaffold/scaffold.go delete mode 100644 pkg/scaffold/secrets.go delete mode 100644 pkg/scaffold/terraform.go delete mode 100644 pkg/wkspace/actions.go delete mode 100644 pkg/wkspace/builder_test.go delete mode 100644 pkg/wkspace/graph.go delete mode 100644 pkg/wkspace/manifest.go delete mode 100644 pkg/wkspace/notes.go diff --git a/cmd/command/clusters/clusters.go b/cmd/command/clusters/clusters.go deleted file mode 100644 index 7c3623d08..000000000 --- a/cmd/command/clusters/clusters.go +++ /dev/null @@ -1,207 +0,0 @@ -package clusters - -import ( - "fmt" - - "github.com/pluralsh/plural-cli/pkg/api" - "github.com/pluralsh/plural-cli/pkg/client" - "github.com/pluralsh/plural-cli/pkg/common" - "github.com/pluralsh/plural-cli/pkg/config" - "github.com/pluralsh/plural-cli/pkg/manifest" - "github.com/pluralsh/plural-cli/pkg/utils" - "github.com/urfave/cli" -) - -type Plural struct { - client.Plural -} - -func Command(clients client.Plural) cli.Command { - p := Plural{ - Plural: clients, - } - return cli.Command{ - Name: "clusters", - Usage: "commands related to managing plural clusters", - Subcommands: p.clusterCommands(), - } -} - -func (p *Plural) clusterCommands() []cli.Command { - return []cli.Command{ - { - Name: "list", - Usage: "lists clusters accessible to your user", - Action: common.LatestVersion(p.listClusters), - }, - { - Name: "transfer", - Usage: "transfers ownership of the current cluster to another", - Action: common.LatestVersion(common.Rooted(p.transferOwnership)), - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "email", - Usage: "the email of the new owner", - }, - }, - }, - { - Name: "view", - Usage: "shows info for a cluster", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "id", - Usage: "the id of the source cluster", - }, - }, - Action: common.LatestVersion(p.showCluster), - }, - { - Name: "depend", - Usage: "have a cluster wait for promotion on another cluster", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "source-id", - Usage: "the id of the source cluster", - }, - cli.StringFlag{ - Name: "dest-id", - Usage: "the id of the cluster waiting for promotion", - }, - }, - Action: common.LatestVersion(p.dependCluster), - }, - { - Name: "promote", - Usage: "promote pending upgrades to your cluster", - Action: common.LatestVersion(p.promoteCluster), - }, - } -} - -func (p *Plural) listClusters(c *cli.Context) error { - p.InitPluralClient() - clusters, err := p.Client.Clusters() - if err != nil { - return err - } - - headers := []string{"ID", "Name", "Provider", "Git Url", "Owner"} - return utils.PrintTable(clusters, headers, func(c *api.Cluster) ([]string, error) { - return []string{c.Id, c.Name, c.Provider, c.GitUrl, c.Owner.Email}, nil - }) -} - -func (p *Plural) transferOwnership(c *cli.Context) error { - p.InitPluralClient() - email := c.String("email") - man, err := manifest.FetchProject() - if err != nil { - return err - } - - if err := p.TransferOwnership(man.Cluster, email); err != nil { - return api.GetErrorResponse(err, "TransferOwnership") - } - - man.Owner.Email = email - if err := man.Flush(); err != nil { - return err - } - - if err := p.AssumeServiceAccount(config.Read(), man); err != nil { - return err - } - - utils.Highlight("rebuilding bootstrap and console to sync your cluster with the new owner:\n") - - for _, app := range []string{"bootstrap", "console"} { - installation, err := p.GetInstallation(app) - if err != nil { - return api.GetErrorResponse(err, "GetInstallation") - } else if installation == nil { - continue - } - - if err := common.DoBuild(p.Client, installation, false); err != nil { - return err - } - } - - utils.Highlight("deploying rebuilt applications\n") - if err := p.Deploy(c); err != nil { - return err - } - - utils.Success("Ownership successfully transferred to %s", email) - return nil -} - -func (p *Plural) showCluster(c *cli.Context) error { - p.InitPluralClient() - id := c.String("id") - if id == "" { - clusters, err := p.Client.Clusters() - if err != nil { - return err - } - - project, err := manifest.FetchProject() - if err != nil { - return err - } - for _, cluster := range clusters { - if cluster.Name == project.Cluster && cluster.Owner.Email == project.Owner.Email { - id = cluster.Id - break - } - } - } - cluster, err := p.Client.Cluster(id) - if err != nil { - return err - } - - fmt.Printf("Cluster %s:\n\n", cluster.Id) - - utils.PrintAttributes(map[string]string{ - "Id": cluster.Id, - "Name": cluster.Name, - "Provider": cluster.Provider, - "Git Url": cluster.GitUrl, - "Owner": cluster.Owner.Email, - }) - - fmt.Println("") - if len(cluster.UpgradeInfo) > 0 { - fmt.Printf("Pending Upgrades:\n\n") - headers := []string{"Repository", "Count"} - return utils.PrintTable(cluster.UpgradeInfo, headers, func(c *api.UpgradeInfo) ([]string, error) { - return []string{c.Installation.Repository.Name, fmt.Sprintf("%d", c.Count)}, nil - }) - } - - fmt.Println("No pending upgrades") - return nil -} - -func (p *Plural) dependCluster(c *cli.Context) error { - p.InitPluralClient() - source, dest := c.String("source-id"), c.String("dest-id") - if err := p.Client.CreateDependency(source, dest); err != nil { - return err - } - - utils.Highlight("Cluster %s will now delegate upgrades to %s", dest, source) - return nil -} - -func (p *Plural) promoteCluster(c *cli.Context) error { - p.InitPluralClient() - if err := p.Client.PromoteCluster(); err != nil { - return err - } - - utils.Success("Upgrades promoted!") - return nil -} diff --git a/cmd/command/plural/plural.go b/cmd/command/plural/plural.go index 51a8e7cc1..474011f7e 100644 --- a/cmd/command/plural/plural.go +++ b/cmd/command/plural/plural.go @@ -6,7 +6,6 @@ import ( "github.com/pluralsh/plural-cli/cmd/command/auth" "github.com/pluralsh/plural-cli/cmd/command/cd" "github.com/pluralsh/plural-cli/cmd/command/clone" - "github.com/pluralsh/plural-cli/cmd/command/clusters" "github.com/pluralsh/plural-cli/cmd/command/config" cryptocmd "github.com/pluralsh/plural-cli/cmd/command/crypto" "github.com/pluralsh/plural-cli/cmd/command/down" @@ -41,19 +40,6 @@ type Plural struct { func (p *Plural) getCommands() []cli.Command { return []cli.Command{ - { - Name: "diff", - Aliases: []string{"df"}, - Usage: "diffs the state of the current workspace with the deployed version and dumps results to diffs/", - ArgsUsage: "APP", - Action: common.LatestVersion(common.HandleDiff), - }, - { - Name: "create", - Usage: "scaffolds the resources needed to create a new plural repository", - Action: common.LatestVersion(common.HandleScaffold), - Category: "Workspace", - }, { Name: "readme", Aliases: []string{"b"}, @@ -126,12 +112,6 @@ func (p *Plural) getCommands() []cli.Command { Action: common.LatestVersion(common.HandleHelmTemplate), Category: "Publishing", }, - { - Name: "changed", - Usage: "shows repos with pending changes", - Action: common.LatestVersion(common.Diffed), - Category: "Workspace", - }, } } @@ -180,7 +160,6 @@ func CreateNewApp(plural *Plural) *cli.App { cd.Command(plural.Plural, plural.HelmConfiguration), config.Command(), cryptocmd.Command(plural.Plural), - clusters.Command(plural.Plural), clone.Command(), down.Command(), ops.Command(plural.Plural), diff --git a/cmd/command/workspace/workspace.go b/cmd/command/workspace/workspace.go index d54fbb0ff..0d7214f2f 100644 --- a/cmd/command/workspace/workspace.go +++ b/cmd/command/workspace/workspace.go @@ -42,18 +42,6 @@ func (p *Plural) workspaceCommands() []cli.Command { Usage: "generates kubernetes credentials for this subworkspace", Action: common.LatestVersion(kubeInit), }, - { - Name: "readme", - Usage: "generate chart readme for an app", - ArgsUsage: "{app}", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "dry-run", - Usage: "output to stdout instead of to a file", - }, - }, - Action: common.LatestVersion(func(c *cli.Context) error { return common.AppReadme(c.Args().Get(0), c.Bool("dry-run")) }), - }, { Name: "helm", Usage: "upgrade/installs the helm chart for this subworkspace", diff --git a/pkg/bundle/installer.go b/pkg/bundle/installer.go deleted file mode 100644 index 1c5e2ceb3..000000000 --- a/pkg/bundle/installer.go +++ /dev/null @@ -1,117 +0,0 @@ -package bundle - -import ( - "fmt" - "os" - - "github.com/inancgumus/screen" - - "github.com/pluralsh/plural-cli/pkg/api" - "github.com/pluralsh/plural-cli/pkg/bundle/tests" - "github.com/pluralsh/plural-cli/pkg/manifest" - "github.com/pluralsh/plural-cli/pkg/utils" -) - -func Install(client api.Client, repo, name string, refresh bool) error { - recipe, err := client.GetRecipe(repo, name) - if err != nil { - return api.GetErrorResponse(err, "GetRecipe") - } - - return doInstall(client, recipe, repo, name, refresh) -} - -func doInstall(client api.Client, recipe *api.Recipe, repo, name string, refresh bool) error { - if recipe.Restricted && os.Getenv("CLOUD_SHELL") == "1" { - return fmt.Errorf("Cannot install this bundle for %s in cloud shell, this is often because it requires a file locally available on your machine like a git ssh key", repo) - } - - path := manifest.ContextPath() - context, err := manifest.ReadContext(path) - if err != nil { - context = manifest.NewContext() - } - - context.AddBundle(repo, name) - - for _, section := range recipe.RecipeSections { - screen.Clear() - screen.MoveTopLeft() - utils.Highlight(section.Repository.Name) - fmt.Printf(" %s\n", section.Repository.Description) - - ctx, ok := context.Configuration[section.Repository.Name] - if !ok { - ctx = map[string]interface{}{} - } - - seen := make(map[string]bool) - - for _, configItem := range section.Configuration { - if seen[configItem.Name] { - continue - } - - if _, ok := ctx[configItem.Name]; ok && !refresh { - continue - } - - seen[configItem.Name] = true - if err := Configure(ctx, configItem, context, repo); err != nil { - context.Configuration[section.Repository.Name] = ctx - if err := context.Write(path); err != nil { - return err - } - return err - } - } - - context.Configuration[section.Repository.Name] = ctx - } - - err = context.Write(path) - if err != nil { - return err - } - - if err := performTests(context, recipe); err != nil { - return err - } - - if err := client.InstallRecipe(recipe.Id); err != nil { - return fmt.Errorf("Install failed, does your plural user have install permissions? error: %w", api.GetErrorResponse(err, "InstallRecipe")) - } - - if recipe.OidcSettings == nil { - return nil - } - - confirm := false - if err := ConfigureOidc(repo, client, recipe, context.Configuration[repo], &confirm); err != nil { - return err - } - - for _, r := range recipe.RecipeDependencies { - repo := r.Repository.Name - if err := ConfigureOidc(repo, client, r, context.Configuration[repo], &confirm); err != nil { - return err - } - } - - return nil -} - -func performTests(ctx *manifest.Context, recipe *api.Recipe) error { - if len(recipe.Tests) == 0 { - return nil - } - - utils.Highlight("Found %d tests to run...\n", len(recipe.Tests)) - for _, test := range recipe.Tests { - if err := tests.Perform(ctx, test); err != nil { - return err - } - } - - return nil -} diff --git a/pkg/client/build.go b/pkg/client/build.go deleted file mode 100644 index e38be2e4c..000000000 --- a/pkg/client/build.go +++ /dev/null @@ -1,149 +0,0 @@ -package client - -import ( - "fmt" - "path/filepath" - "strings" - - "github.com/pluralsh/plural-cli/pkg/application" - "github.com/pluralsh/plural-cli/pkg/common" - "github.com/pluralsh/plural-cli/pkg/executor" - "github.com/pluralsh/plural-cli/pkg/kubernetes" - "github.com/pluralsh/plural-cli/pkg/manifest" - "github.com/pluralsh/plural-cli/pkg/scaffold" - "github.com/pluralsh/plural-cli/pkg/utils" - "github.com/pluralsh/plural-cli/pkg/utils/git" - "github.com/pluralsh/plural-cli/pkg/utils/pathing" - "github.com/pluralsh/polly/algorithms" - "github.com/pluralsh/polly/containers" - "github.com/urfave/cli" - - "github.com/pluralsh/plural-cli/pkg/api" - "github.com/pluralsh/plural-cli/pkg/wkspace" -) - -const Bootstrap = "bootstrap" - -func (p *Plural) Deploy(c *cli.Context) error { - p.InitPluralClient() - verbose := c.Bool("verbose") - repoRoot, err := git.Root() - if err != nil { - return err - } - - // project, err := manifest.FetchProject() - // if err != nil { - // return err - // } - - var sorted []string - switch { - case len(c.StringSlice("from")) > 0: - sorted, err = wkspace.AllDependencies(c.StringSlice("from")) - case c.Bool("all"): - sorted, err = p.allSortedRepos() - default: - sorted, err = getSortedNames(true) - } - if err != nil { - return err - } - - fmt.Printf("Deploying applications [%s] in topological order\n\n", strings.Join(sorted, ", ")) - - ignoreConsole := c.Bool("ignore-console") - for _, repo := range sorted { - if ignoreConsole && (repo == "console" || repo == Bootstrap) { - continue - } - - execution, err := executor.GetExecution(pathing.SanitizeFilepath(filepath.Join(repoRoot, repo)), "deploy") - if err != nil { - return err - } - - if err := execution.Execute("deploying", verbose); err != nil { - utils.Note("It looks like your deployment failed. This may be a transient issue and rerunning the `plural deploy` command may resolve it. Or, feel free to reach out to us on discord (https://discord.gg/bEBAMXV64s) or Intercom and we should be able to help you out\n") - return err - } - - fmt.Printf("\n") - - installation, err := p.GetInstallation(repo) - if err != nil { - return api.GetErrorResponse(err, "GetInstallation") - } - if installation == nil { - return fmt.Errorf("The %s was unistalled, run `plural bundle install %s ` ", repo, repo) - } - - if err := p.Client.MarkSynced(repo); err != nil { - utils.Warn("failed to mark %s as synced, this is not a critical error but might drift state in our api, you can run `plural repos synced %s` to mark it manually", repo, repo) - } - - if c.Bool("silence") { - continue - } - - if man, err := fetchManifest(repo); err == nil && man.Wait { - if kubeConf, err := kubernetes.KubeConfig(); err == nil { - fmt.Printf("Waiting for %s to become ready...\n", repo) - if err := application.SilentWait(kubeConf, repo); err != nil { - return err - } - fmt.Println("") - } - } - - if err := scaffold.Notes(installation); err != nil { - return err - } - } - - utils.Highlight("\n==> Commit and push your changes to record your deployment\n\n") - - if commit := common.CommitMsg(c); commit != "" { - utils.Highlight("Pushing upstream...\n") - return git.Sync(repoRoot, commit, c.Bool("force")) - } - - return nil -} - -func (p *Plural) allSortedRepos() ([]string, error) { - p.InitPluralClient() - insts, err := p.GetInstallations() - if err != nil { - return nil, api.GetErrorResponse(err, "GetInstallations") - } - - return wkspace.SortAndFilter(insts) -} -func getSortedNames(filter bool) ([]string, error) { - diffed, err := wkspace.DiffedRepos() - if err != nil { - return nil, err - } - - sorted, err := wkspace.TopSortNames(diffed) - if err != nil { - return nil, err - } - - if filter { - repos := containers.ToSet(diffed) - return algorithms.Filter(sorted, repos.Has), nil - } - - return sorted, nil -} - -func fetchManifest(repo string) (*manifest.Manifest, error) { - p, err := manifest.ManifestPath(repo) - if err != nil { - return nil, err - } - - return manifest.Read(p) -} diff --git a/pkg/common/app.go b/pkg/common/app.go index 77287f691..9e76c239b 100644 --- a/pkg/common/app.go +++ b/pkg/common/app.go @@ -2,35 +2,12 @@ package common import ( "fmt" - "os/exec" - "strings" "github.com/pluralsh/plural-cli/pkg/up" "github.com/urfave/cli" - - "github.com/pluralsh/plural-cli/pkg/config" - "github.com/pluralsh/plural-cli/pkg/utils" ) -func HandleInfo(c *cli.Context) error { - repo := c.Args().Get(0) - conf := config.Read() - - _, err := exec.LookPath("k9s") - if err != nil { - utils.LogError().Println(err) - if strings.Contains(err.Error(), exec.ErrNotFound.Error()) { - utils.Error("Application k9s not installed.\n") - fmt.Println("Please install it first from here: https://k9scli.io/topics/install/ and try again") - return nil - } - } - - cmd := exec.Command("k9s", "-n", conf.Namespace(repo)) - return cmd.Run() -} - func HandleDown(_ *cli.Context) error { if !Affirm(AffirmDown, "PLURAL_DOWN_AFFIRM_DESTROY") { return fmt.Errorf("cancelled destroy") diff --git a/pkg/common/apply.go b/pkg/common/apply.go deleted file mode 100644 index 82362e5a8..000000000 --- a/pkg/common/apply.go +++ /dev/null @@ -1,33 +0,0 @@ -package common - -import ( - "os" - "path/filepath" - - "github.com/pluralsh/plural-cli/pkg/pluralfile" - "github.com/pluralsh/plural-cli/pkg/utils/pathing" - "github.com/urfave/cli" -) - -func Apply(c *cli.Context) error { - path, _ := os.Getwd() - var file = pathing.SanitizeFilepath(filepath.Join(path, "Pluralfile")) - if c.IsSet("file") { - file, _ = filepath.Abs(c.String("file")) - } - - if err := os.Chdir(filepath.Dir(file)); err != nil { - return err - } - - plrl, err := pluralfile.Parse(file) - if err != nil { - return err - } - - lock, err := plrl.Lock(file) - if err != nil { - return err - } - return plrl.Execute(file, lock) -} diff --git a/pkg/common/common.go b/pkg/common/common.go index e147e3e22..cf705a70c 100644 --- a/pkg/common/common.go +++ b/pkg/common/common.go @@ -19,7 +19,6 @@ import ( "github.com/pluralsh/plural-cli/pkg/wkspace" "github.com/urfave/cli" - "github.com/pluralsh/plural-cli/pkg/scaffold" "github.com/pluralsh/plural-cli/pkg/utils/git" ) @@ -27,56 +26,6 @@ var ( loggedIn = false ) -func AppReadme(name string, dryRun bool) error { - repoRoot, err := git.Root() - if err != nil { - return err - } - - dir := filepath.Join(repoRoot, name, "helm", name) - return scaffold.Readme(dir, dryRun) -} - -func DoBuild(client api.Client, installation *api.Installation, force bool) error { - repoName := installation.Repository.Name - fmt.Printf("Building workspace for %s\n", repoName) - - if !wkspace.Configured(repoName) { - fmt.Printf("You have not locally configured %s but have it registered as an installation in our api, ", repoName) - fmt.Printf("either delete it with `plural apps uninstall %s` or install it locally via a bundle in `plural bundle list %s`\n", repoName, repoName) - return nil - } - - workspace, err := wkspace.New(client, installation) - if err != nil { - return err - } - - vsn, ok := workspace.RequiredCliVsn() - if ok && !VersionValid(vsn) { - return fmt.Errorf("Your cli version is not sufficient to complete this build, please update to at least %s", vsn) - } - - if err := workspace.Prepare(); err != nil { - return err - } - - build, err := scaffold.Scaffolds(workspace) - if err != nil { - return err - } - - err = build.Execute(workspace, force) - if err == nil { - utils.Success("Finished building %s\n\n", repoName) - } - - workspace.PrintLinks() - - AppReadme(repoName, false) // nolint:errcheck - return err -} - func HandleLogin(c *cli.Context) error { if loggedIn { return nil diff --git a/pkg/common/diff.go b/pkg/common/diff.go deleted file mode 100644 index e611c8706..000000000 --- a/pkg/common/diff.go +++ /dev/null @@ -1,75 +0,0 @@ -package common - -import ( - "fmt" - "path/filepath" - "strings" - - "github.com/pluralsh/plural-cli/pkg/diff" - "github.com/pluralsh/plural-cli/pkg/utils/git" - "github.com/pluralsh/plural-cli/pkg/utils/pathing" - "github.com/pluralsh/plural-cli/pkg/wkspace" - "github.com/pluralsh/polly/algorithms" - "github.com/pluralsh/polly/containers" - "github.com/urfave/cli" -) - -func Diffed(_ *cli.Context) error { - diffed, err := wkspace.DiffedRepos() - if err != nil { - return err - } - - for _, d := range diffed { - fmt.Println(d) - } - - return nil -} - -func getSortedNames(filter bool) ([]string, error) { - diffed, err := wkspace.DiffedRepos() - if err != nil { - return nil, err - } - - sorted, err := wkspace.TopSortNames(diffed) - if err != nil { - return nil, err - } - - if filter { - repos := containers.ToSet(diffed) - return algorithms.Filter(sorted, repos.Has), nil - } - - return sorted, nil -} - -func HandleDiff(_ *cli.Context) error { - repoRoot, err := git.Root() - if err != nil { - return err - } - - sorted, err := getSortedNames(true) - if err != nil { - return err - } - - fmt.Printf("Diffing applications [%s] in topological order\n\n", strings.Join(sorted, ", ")) - - for _, repo := range sorted { - d, err := diff.GetDiff(pathing.SanitizeFilepath(filepath.Join(repoRoot, repo)), "diff") - if err != nil { - return err - } - - if err := d.Execute(); err != nil { - return err - } - - fmt.Printf("\n") - } - return nil -} diff --git a/pkg/common/scaffold.go b/pkg/common/scaffold.go deleted file mode 100644 index 1d09c4de0..000000000 --- a/pkg/common/scaffold.go +++ /dev/null @@ -1,12 +0,0 @@ -package common - -import ( - "github.com/pluralsh/plural-cli/pkg/api" - "github.com/pluralsh/plural-cli/pkg/scaffold" - "github.com/urfave/cli" -) - -func HandleScaffold(c *cli.Context) error { - client := api.NewClient() - return scaffold.ApplicationScaffold(client) -} diff --git a/pkg/common/validation.go b/pkg/common/validation.go index a72cb88e4..9070cb6e5 100644 --- a/pkg/common/validation.go +++ b/pkg/common/validation.go @@ -5,17 +5,13 @@ import ( "os" "github.com/pluralsh/plural-cli/pkg/provider" - "github.com/pluralsh/plural-cli/pkg/utils/errors" "github.com/pluralsh/polly/algorithms" "github.com/AlecAivazis/survey/v2" "github.com/pluralsh/plural-cli/pkg/api" "github.com/pluralsh/plural-cli/pkg/config" "github.com/pluralsh/plural-cli/pkg/executor" - "github.com/pluralsh/plural-cli/pkg/manifest" "github.com/pluralsh/plural-cli/pkg/utils" - "github.com/pluralsh/plural-cli/pkg/utils/git" - "github.com/pluralsh/plural-cli/pkg/utils/pathing" "github.com/urfave/cli" ) @@ -40,26 +36,6 @@ func RequireArgs(fn func(*cli.Context) error, args []string) func(*cli.Context) } } -func Rooted(fn func(*cli.Context) error) func(*cli.Context) error { - return func(c *cli.Context) error { - if err := RepoRoot(); err != nil { - return err - } - - return fn(c) - } -} - -func Owned(fn func(*cli.Context) error) func(*cli.Context) error { - return func(c *cli.Context) error { - if err := ValidateOwner(); err != nil { - return err - } - - return fn(c) - } -} - func Affirmed(fn func(*cli.Context) error, msg string, envKey string) func(*cli.Context) error { return func(c *cli.Context) error { if !Affirm(msg, envKey) { @@ -100,27 +76,6 @@ func Tracked(fn func(*cli.Context) error, event string) func(*cli.Context) error } } -func ValidateOwner() error { - path := manifest.ProjectManifestPath() - project, err := manifest.ReadProject(path) - if err != nil { - return fmt.Errorf("Your workspace hasn't been configured. Try running `plural init`.") - } - - if owner := project.Owner; owner != nil { - conf := config.Read() - if owner.Endpoint != conf.Endpoint { - return fmt.Errorf( - "The owner of this project is actually %s; plural environment = %s", - owner.Email, - config.PluralUrl(owner.Endpoint), - ) - } - } - - return nil -} - func Confirm(msg string, envKey string) bool { res := true conf, ok := utils.GetEnvBoolValue(envKey) @@ -147,26 +102,6 @@ func Affirm(msg string, envKey string) bool { return res } -func RepoRoot() error { - dir, err := os.Getwd() - if err != nil { - return err - } - // santiize the filepath, respecting the OS - dir = pathing.SanitizeFilepath(dir) - - root, err := git.Root() - if err != nil { - return err - } - - if root != dir { - return fmt.Errorf("You must run this command at the root of your git repository") - } - - return nil -} - func LatestVersion(fn func(*cli.Context) error) func(*cli.Context) error { return func(c *cli.Context) error { if os.Getenv("PLURAL_CONSOLE") != "1" && os.Getenv("CLOUD_SHELL") != "1" && algorithms.Coinflip(1, 5) { @@ -200,17 +135,6 @@ func InitKubeconfig(fn func(*cli.Context) error) func(*cli.Context) error { } } -func RequireKind(fn func(*cli.Context) error) func(*cli.Context) error { - return func(c *cli.Context) error { - exists, _ := utils.Which("kind") - if !exists { - return fmt.Errorf("The kind CLI is not installed") - } - - return fn(c) - } -} - func CommitMsg(c *cli.Context) string { if commit := c.String("commit"); commit != "" { return commit @@ -226,20 +150,3 @@ func CommitMsg(c *cli.Context) string { return "" } - -func UpstreamSynced(fn func(*cli.Context) error) func(*cli.Context) error { - return func(c *cli.Context) error { - changed, sha, err := git.HasUpstreamChanges() - if err != nil { - utils.LogError().Println(err) - return errors.ErrorWrap(ErrNoGit, "Failed to get git information") - } - - force := c.Bool("force") - if !changed && !force { - return errors.ErrorWrap(ErrRemoteDiff, fmt.Sprintf("Expecting HEAD at commit=%s", sha)) - } - - return fn(c) - } -} diff --git a/pkg/common/version.go b/pkg/common/version.go index 100e23967..015802add 100644 --- a/pkg/common/version.go +++ b/pkg/common/version.go @@ -6,8 +6,6 @@ import ( "runtime" "strings" - "golang.org/x/mod/semver" - "github.com/pluralsh/plural-cli/pkg/utils" "github.com/urfave/cli" ) @@ -22,14 +20,6 @@ var ( Date = "" ) -func VersionValid(vsn string) bool { - current := Version - if !strings.HasPrefix(current, "v") { - current = fmt.Sprintf("v%s", current) - } - return semver.Compare(vsn, current) <= 0 -} - func checkRecency() error { if os.Getenv("CLOUD_SHELL") == "1" || os.Getenv("PLURAL_CONSOLE") == "1" { return nil diff --git a/pkg/helm/helm.go b/pkg/helm/helm.go index 807fcc72b..01c9543a3 100644 --- a/pkg/helm/helm.go +++ b/pkg/helm/helm.go @@ -72,25 +72,6 @@ func Template(conf *action.Configuration, name, namespace, path string, isUpgrad return manifests.Bytes(), nil } -func Lint(path, namespace string, values map[string]interface{}) error { - client := action.NewLint() - client.Namespace = namespace - result := client.Run([]string{path}, values) - // All the Errors that are generated by a chart - // that failed a lint will be included in the - // results.Messages so we only need to print - // the Errors if there are no Messages. - if len(result.Messages) == 0 { - for _, err := range result.Errors { - fmt.Printf("Error %s\n", err) - } - } - if len(result.Errors) != 0 { - return fmt.Errorf("failed") - } - return nil -} - func AddRepo(repoName, repoUrl string) error { repoFile := getEnvVar("HELM_REPOSITORY_CONFIG", helmpath.ConfigPath("repositories.yaml")) err := os.MkdirAll(filepath.Dir(repoFile), os.ModePerm) diff --git a/pkg/logs/api.go b/pkg/logs/api.go deleted file mode 100644 index 1f3bbaa06..000000000 --- a/pkg/logs/api.go +++ /dev/null @@ -1,33 +0,0 @@ -package logs - -import ( - "github.com/pluralsh/plural-cli/pkg/kubernetes" - "github.com/pluralsh/plural-cli/pkg/kubernetes/logs" - "github.com/pluralsh/plural-cli/pkg/utils" - "github.com/pluralsh/plural-operator/apis/platform/v1alpha1" -) - -func List(kube kubernetes.Kube, namespace string) (*v1alpha1.LogTailList, error) { - return kube.LogTailList(namespace) -} - -func Print(tails *v1alpha1.LogTailList) error { - headers := []string{"Name", "Follow", "Target"} - return utils.PrintTable[v1alpha1.LogTail](tails.Items, headers, func(log v1alpha1.LogTail) ([]string, error) { - follow := "False" - if log.Spec.Follow { - follow = "True" - } - - return []string{log.Name, follow, log.Spec.Target}, nil - }) -} - -func Tail(kube kubernetes.Kube, namespace string, name string) error { - tail, err := kube.LogTail(namespace, name) - if err != nil { - return err - } - - return logs.Logs(namespace, tail.Spec.Target, int64(tail.Spec.Limit)) -} diff --git a/pkg/output/terraform.go b/pkg/output/terraform.go deleted file mode 100644 index 1dd00521d..000000000 --- a/pkg/output/terraform.go +++ /dev/null @@ -1,32 +0,0 @@ -package output - -import ( - "encoding/json" - "os/exec" -) - -type TerraformOutputItem struct { - Value interface{} - Type interface{} -} - -func TerraformOutput(path string) (out map[string]interface{}, err error) { - cmd := exec.Command("terraform", "output", "-json") - cmd.Dir = path - res, err := cmd.Output() - if err != nil { - return - } - - outType := make(map[string]TerraformOutputItem) - err = json.Unmarshal(res, &outType) - if err != nil { - return - } - - out = make(map[string]interface{}) - for key, val := range outType { - out[key] = val.Value - } - return -} diff --git a/pkg/scaffold/application.go b/pkg/scaffold/application.go deleted file mode 100644 index f85597eb8..000000000 --- a/pkg/scaffold/application.go +++ /dev/null @@ -1,66 +0,0 @@ -package scaffold - -import ( - "os" - "path/filepath" - - "github.com/imdario/mergo" - "github.com/pluralsh/plural-cli/pkg/output" - "github.com/pluralsh/plural-cli/pkg/utils" - "github.com/pluralsh/plural-cli/pkg/utils/git" - "github.com/pluralsh/plural-cli/pkg/utils/pathing" - "sigs.k8s.io/yaml" -) - -type Applications struct { - Root string -} - -func BuildApplications(root string) *Applications { - return &Applications{Root: root} -} - -func NewApplications() (*Applications, error) { - root, err := git.Root() - if err != nil { - return nil, err - } - - return BuildApplications(root), nil -} - -func (apps *Applications) HelmValues(app string) (map[string]interface{}, error) { - valuesFile := pathing.SanitizeFilepath(filepath.Join(apps.Root, app, "helm", app, "values.yaml")) - vals := make(map[string]interface{}) - valsContent, err := os.ReadFile(valuesFile) - if err != nil { - return nil, err - } - if err := yaml.Unmarshal(valsContent, &vals); err != nil { - return nil, err - } - - defaultValuesFile := pathing.SanitizeFilepath(filepath.Join(apps.Root, app, "helm", app, "default-values.yaml")) - defaultVals := make(map[string]interface{}) - if utils.Exists(defaultValuesFile) { - defaultValsContent, err := os.ReadFile(defaultValuesFile) - if err != nil { - return nil, err - } - if err := yaml.Unmarshal(defaultValsContent, &defaultVals); err != nil { - return nil, err - } - } - - err = mergo.Merge(&defaultVals, vals, mergo.WithOverride) - if err != nil { - return nil, err - } - - return defaultVals, err -} - -func (apps *Applications) TerraformValues(app string) (map[string]interface{}, error) { - out, err := output.Read(pathing.SanitizeFilepath(filepath.Join(apps.Root, app, "output.yaml"))) - return out.Terraform, err -} diff --git a/pkg/scaffold/application_test.go b/pkg/scaffold/application_test.go deleted file mode 100644 index d5c56fb8b..000000000 --- a/pkg/scaffold/application_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package scaffold_test - -import ( - "os" - "path/filepath" - "testing" - - "github.com/pluralsh/plural-cli/pkg/scaffold" - "github.com/stretchr/testify/assert" -) - -const values_file = `console: - ingress: - console_dns: console.onplural.sh -` - -const defaultValues_file = `console: - enabled: true - ingress: - annotations: - external-dns.alpha.kubernetes.io/target: 127.0.0.1 - console_dns: console.onplural.sh - license: abc - provider: kind -` - -func TestListRepositories(t *testing.T) { - tests := []struct { - name string - appName string - expectedResponse map[string]interface{} - }{ - { - name: `test HelmValues`, - appName: "test", - expectedResponse: map[string]interface{}{ - "console": map[string]interface{}{ - "enabled": true, - "ingress": map[string]interface{}{ - "annotations": map[string]interface{}{ - "external-dns.alpha.kubernetes.io/target": "127.0.0.1", - }, - "console_dns": "console.onplural.sh", - }, - "license": "abc", - "provider": "kind", - }, - }, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - dir, err := os.MkdirTemp("", "config") - assert.NoError(t, err) - defer os.RemoveAll(dir) - - dirPath := filepath.Join(dir, test.appName, "helm", test.appName) - err = os.MkdirAll(dirPath, os.ModePerm) - assert.NoError(t, err) - err = os.WriteFile(filepath.Join(dirPath, "default-values.yaml"), []byte(defaultValues_file), 0644) - assert.NoError(t, err) - err = os.WriteFile(filepath.Join(dirPath, "values.yaml"), []byte(values_file), 0644) - assert.NoError(t, err) - - application := scaffold.Applications{ - Root: dir, - } - res, err := application.HelmValues("test") - - assert.NoError(t, err) - assert.Equal(t, test.expectedResponse, res) - }) - } -} diff --git a/pkg/scaffold/constants.go b/pkg/scaffold/constants.go deleted file mode 100644 index aa3401772..000000000 --- a/pkg/scaffold/constants.go +++ /dev/null @@ -1,116 +0,0 @@ -package scaffold - -import ( - "path/filepath" -) - -const ( - defaultNotes = `Your {{ .Release.Name }} installation` - sep = string(filepath.Separator) - - defaultIgnore = `# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ -` - defaultApplication = `apiVersion: app.k8s.io/v1beta1 -kind: Application -metadata: - name: {{ .Name }} -spec: - selector: - matchLabels: {} - componentKinds: - - group: v1 - kind: Service - - group: networking.k8s.io - kind: Ingress - - group: cert-manager.io - kind: Certificate - - group: apps - kind: StatefulSet - - group: apps - kind: Deployment - - group: batch - kind: CronJob - - group: batch - kind: Job - descriptor: - type: {{ .Name }} - version: "{{ .Version }}" - description: {{ .Description }} - icons: - - src: {{ .Icon }} - {{ if .DarkIcon }} - - src: {{ .DarkIcon }} - {{ end }} -` - - appTemplate = ` - {{- if .Values.global }} - {{- if .Values.global.application }} - {{- if .Values.global.application.links }} - links: - {{ toYaml .Values.global.application.links | nindent 6 }} - {{- end }} - {{- if .Values.global.application.info }} - info: - {{ toYaml .Values.global.application.info | nindent 4 }} - {{- end }} - {{- end }} - {{- end }} -` - - licenseSecret = `apiVersion: v1 -kind: Secret -metadata: - name: plural-license-secret -stringData: - license: {{ .Values.plrl.license }} -` - - license = `apiVersion: platform.plural.sh/v1alpha1 -kind: License -metadata: - name: %s -spec: - secretRef: - name: plural-license-secret - key: license -` - - // ChartfileName is the default Chart file name. - ChartfileName = "Chart.yaml" - // ValuesfileName is the default values file name. - TemplatesDir = "templates" - // ChartsDir is the relative directory name for charts dependencies. - ChartsDir = "charts" - // IgnorefileName is the name of the Helm ignore file. - IgnorefileName = ".helmignore" - // NotesName is the name of the example NOTES.txt file. - NotesName = TemplatesDir + sep + "NOTES.txt" - // file to put the default application resource in - ApplicationName = TemplatesDir + sep + "application.yaml" - // file to put the license secret in - LicenseSecretName = TemplatesDir + sep + "secret.yaml" - // file to put the license crd in - LicenseCrdName = TemplatesDir + sep + "license.yaml" -) diff --git a/pkg/scaffold/crd.go b/pkg/scaffold/crd.go deleted file mode 100644 index 3a4ed6e8b..000000000 --- a/pkg/scaffold/crd.go +++ /dev/null @@ -1,52 +0,0 @@ -package scaffold - -import ( - "fmt" - "io" - "net/http" - "os" - "path/filepath" - - "github.com/pluralsh/plural-cli/pkg/api" - "github.com/pluralsh/plural-cli/pkg/utils" - "github.com/pluralsh/plural-cli/pkg/utils/pathing" - "github.com/pluralsh/plural-cli/pkg/wkspace" -) - -func (s *Scaffold) buildCrds(wk *wkspace.Workspace) error { - utils.Highlight("syncing crds") - if err := os.RemoveAll(s.Root); err != nil { - return err - } - - if err := os.MkdirAll(s.Root, os.ModePerm); err != nil { - return err - } - - for _, chartInst := range wk.Charts { - for _, crd := range chartInst.Version.Crds { - utils.Highlight(".") - if err := writeCrd(s.Root, &crd); err != nil { - fmt.Print("\n") - return err - } - } - } - - utils.Success("\u2713\n") - return nil -} - -func writeCrd(path string, crd *api.Crd) error { - resp, err := http.Get(crd.Blob) - if err != nil { - return err - } - - contents, err := io.ReadAll(resp.Body) - if err != nil { - return err - } - - return os.WriteFile(pathing.SanitizeFilepath(filepath.Join(path, crd.Name)), contents, 0644) -} diff --git a/pkg/scaffold/creator.go b/pkg/scaffold/creator.go deleted file mode 100644 index cdfde92ae..000000000 --- a/pkg/scaffold/creator.go +++ /dev/null @@ -1,102 +0,0 @@ -package scaffold - -import ( - "os" - "path/filepath" - - "github.com/AlecAivazis/survey/v2" - "github.com/pluralsh/plural-cli/pkg/api" - "github.com/pluralsh/plural-cli/pkg/utils" - "github.com/pluralsh/plural-cli/pkg/utils/pathing" - "helm.sh/helm/v3/pkg/chartutil" -) - -var categories = []string{ - "data", - "productivity", - "devops", - "database", - "messaging", - "security", - "network", -} - -var scaffoldSurvey = []*survey.Question{ - { - Name: "application", - Prompt: &survey.Input{Message: "Enter the name of your application:"}, - Validate: utils.ValidateAlphaNumeric, - }, - { - Name: "publisher", - Prompt: &survey.Input{Message: "Enter the name of your publisher:"}, - Validate: survey.Required, - }, - { - Name: "category", - Prompt: &survey.Select{ - Message: "Enter the category for your application:", - Options: categories, - }, - Validate: survey.Required, - }, - { - Name: "postgres", - Prompt: &survey.Confirm{Message: "Will your application need a postgres database?"}, - Validate: survey.Required, - }, - { - Name: "ingress", - Prompt: &survey.Confirm{Message: "Does your application need an ingress?"}, - Validate: survey.Required, - }, -} - -func ApplicationScaffold(client api.Client) error { - input := api.ScaffoldInputs{} - if err := survey.Ask(scaffoldSurvey, &input); err != nil { - return err - } - - scaffolds, err := client.Scaffolds(&input) - if err != nil { - return api.GetErrorResponse(err, "Scaffolds") - } - - app := input.Application - helmPath := pathing.SanitizeFilepath(filepath.Join(app, "helm")) - pwd, err := os.Getwd() - if err != nil { - return err - } - - if err := os.MkdirAll(helmPath, 0755); err != nil { - return err - } - - if err := os.Chdir(helmPath); err != nil { - return err - } - - if err := createHelm(app); err != nil { - return err - } - - if err := os.Chdir(pathing.SanitizeFilepath(filepath.Join(pwd, app))); err != nil { - return err - } - - for _, scaffold := range scaffolds { - if err := utils.WriteFile(scaffold.Path, []byte(scaffold.Content)); err != nil { - return err - } - } - - return nil -} - -func createHelm(name string) error { - chartname := filepath.Base(name) - _, err := chartutil.Create(chartname, filepath.Dir(name)) - return err -} diff --git a/pkg/scaffold/default.go b/pkg/scaffold/default.go deleted file mode 100644 index b52b41abc..000000000 --- a/pkg/scaffold/default.go +++ /dev/null @@ -1,59 +0,0 @@ -package scaffold - -import ( - "os" - "path/filepath" - - "github.com/hashicorp/hcl" - "github.com/pluralsh/plural-cli/pkg/executor" - "github.com/pluralsh/plural-cli/pkg/utils/pathing" - "github.com/pluralsh/plural-cli/pkg/wkspace" -) - -func Read(path string) (*Build, error) { - fullpath := pathing.SanitizeFilepath(filepath.Join(path, "build.hcl")) - contents, err := os.ReadFile(fullpath) - build := Build{} - if err != nil { - return &build, err - } - - err = hcl.Decode(&build, string(contents)) - if err != nil { - return &build, err - } - - return &build, nil -} - -func Default(w *wkspace.Workspace, name string) (b *Build) { - return &Build{ - Metadata: &Metadata{Name: name}, - Scaffolds: []*Scaffold{ - { - Name: "terraform", - Path: "terraform", - Type: TF, - }, - { - Name: "crds", - Type: CRD, - Path: "crds", - }, - { - Name: "helm", - Type: HELM, - Path: pathing.SanitizeFilepath(filepath.Join("helm", name)), - Preflight: []*executor.Step{ - { - Name: "update-deps", - Command: "plural", - Args: []string{"wkspace", "helm-deps"}, - Target: "Chart.yaml", - Sha: "", - }, - }, - }, - }, - } -} diff --git a/pkg/scaffold/helm.go b/pkg/scaffold/helm.go deleted file mode 100644 index 26204d60c..000000000 --- a/pkg/scaffold/helm.go +++ /dev/null @@ -1,431 +0,0 @@ -package scaffold - -import ( - "bytes" - "fmt" - "os" - "path/filepath" - "sort" - "strings" - ttpl "text/template" - - "gopkg.in/yaml.v2" - - "github.com/pluralsh/plural-cli/pkg/api" - "github.com/pluralsh/plural-cli/pkg/config" - "github.com/pluralsh/plural-cli/pkg/manifest" - "github.com/pluralsh/plural-cli/pkg/provider" - scftmpl "github.com/pluralsh/plural-cli/pkg/scaffold/template" - "github.com/pluralsh/plural-cli/pkg/template" - "github.com/pluralsh/plural-cli/pkg/utils" - "github.com/pluralsh/plural-cli/pkg/utils/errors" - "github.com/pluralsh/plural-cli/pkg/utils/git" - "github.com/pluralsh/plural-cli/pkg/utils/pathing" - "github.com/pluralsh/plural-cli/pkg/wkspace" -) - -type dependency struct { - Name string - Version string - Repository string - Condition string -} - -type chart struct { - ApiVersion string `yaml:"apiVersion"` - Name string - Description string - Version string - AppVersion string `yaml:"appVersion"` - Dependencies []dependency -} - -func (s *Scaffold) handleHelm(wk *wkspace.Workspace) error { - if err := s.createChart(wk); err != nil { - return err - } - - if err := s.buildChartValues(wk); err != nil { - return err - } - - return nil -} - -func (s *Scaffold) chartDependencies(w *wkspace.Workspace) []dependency { - dependencies := make([]dependency, len(w.Charts)) - repo := w.Installation.Repository - for i, chartInstallation := range w.Charts { - dependencies[i] = dependency{ - chartInstallation.Chart.Name, - chartInstallation.Version.Version, - repoUrl(w, repo.Name, chartInstallation.Chart.Name), - fmt.Sprintf("%s.enabled", chartInstallation.Chart.Name), - } - } - sort.SliceStable(dependencies, func(i, j int) bool { - return dependencies[i].Name < dependencies[j].Name - }) - return dependencies -} - -func Notes(installation *api.Installation) error { - repoRoot, err := git.Root() - if err != nil { - return err - } - - if installation.Repository != nil && installation.Repository.Notes == "" { - return nil - } - - context, err := manifest.ReadContext(manifest.ContextPath()) - if err != nil { - return err - } - - prov, err := provider.GetProvider() - if err != nil { - return err - } - - repo := installation.Repository.Name - ctx, _ := context.Repo(installation.Repository.Name) - - vals := map[string]interface{}{ - "Values": ctx, - "Configuration": context.Configuration, - "License": installation.LicenseKey, - "OIDC": installation.OIDCProvider, - "Region": prov.Region(), - "Project": prov.Project(), - "Cluster": prov.Cluster(), - "Config": config.Read(), - "Provider": prov.Name(), - "Context": prov.Context(), - "Applications": BuildApplications(repoRoot), - } - - if context.Globals != nil { - vals["Globals"] = context.Globals - } - - if context.SMTP != nil { - vals["SMTP"] = context.SMTP.Configuration() - } - - if installation.AcmeKeyId != "" { - vals["Acme"] = map[string]string{ - "KeyId": installation.AcmeKeyId, - "Secret": installation.AcmeSecret, - } - } - - apps := &Applications{Root: repoRoot} - values, err := apps.HelmValues(repo) - if err != nil { - return err - } - - for k, v := range values { - vals[k] = v - } - - tmpl, err := template.MakeTemplate(installation.Repository.Notes) - if err != nil { - return err - } - - var buf bytes.Buffer - buf.Grow(5 * 1024) - if err := tmpl.Execute(&buf, vals); err != nil { - return err - } - - fmt.Println(buf.String()) - return nil -} - -func (s *Scaffold) buildChartValues(w *wkspace.Workspace) error { - ctx, _ := w.Context.Repo(w.Installation.Repository.Name) - valuesFile := pathing.SanitizeFilepath(filepath.Join(s.Root, "values.yaml")) - defaultValuesFile := pathing.SanitizeFilepath(filepath.Join(s.Root, "default-values.yaml")) - defaultPrevVals, _ := prevValues(defaultValuesFile) - prevVals, _ := prevValues(valuesFile) - - if !utils.Exists(valuesFile) { - if err := os.WriteFile(valuesFile, []byte("{}\n"), 0644); err != nil { - return err - } - } - - conf := config.Read() - - apps, err := NewApplications() - if err != nil { - return err - } - - proj, err := manifest.FetchProject() - if err != nil { - return err - } - - vals := map[string]interface{}{ - "Values": ctx, - "Configuration": w.Context.Configuration, - "License": w.Installation.LicenseKey, - "OIDC": w.Installation.OIDCProvider, - "Region": w.Provider.Region(), - "Project": w.Provider.Project(), - "Cluster": w.Provider.Cluster(), - "Config": conf, - "Provider": w.Provider.Name(), - "Context": w.Provider.Context(), - "Network": proj.Network, - "Applications": apps, - } - - if proj.AvailabilityZones != nil { - vals["AvailabilityZones"] = proj.AvailabilityZones - } - - if w.Context.SMTP != nil { - vals["SMTP"] = w.Context.SMTP.Configuration() - } - - if w.Context.Globals != nil { - vals["Globals"] = w.Context.Globals - } - - if w.Installation.AcmeKeyId != "" { - vals["Acme"] = map[string]string{ - "KeyId": w.Installation.AcmeKeyId, - "Secret": w.Installation.AcmeSecret, - } - } - - // get previous values from default-values.yaml if exists otherwise from values.yaml - if utils.Exists(defaultValuesFile) { - for k, v := range defaultPrevVals { - vals[k] = v - } - } else { - for k, v := range prevVals { - vals[k] = v - } - } - defaultValues, err := scftmpl.BuildValuesFromTemplate(vals, w) - if err != nil { - return err - } - - io, err := yaml.Marshal(defaultValues) - if err != nil { - return err - } - - // TODO: Remove this after testing. It is deprecated as values.yaml migration should not longer be required. - // mapValues, err := getValues(valuesFile) - // if err != nil { - // return err - // } - // patchValues, err := utils.PatchInterfaceMap(defaultValues, mapValues) - // if err != nil { - // return err - // } - // - // values, err := yaml.Marshal(patchValues) - // if err != nil { - // return err - // } - // if err := utils.WriteFile(valuesFile, values); err != nil { - // return err - // } - - return utils.WriteFile(defaultValuesFile, io) -} - -//nolint:golint,unused -func getValues(path string) (map[string]map[string]interface{}, error) { - values := map[string]map[string]interface{}{} - valuesFromFile, err := os.ReadFile(path) - if err != nil { - return nil, err - } - if err := yaml.Unmarshal(valuesFromFile, &values); err != nil { - return nil, err - } - return values, nil -} - -func prevValues(filename string) (map[string]map[string]interface{}, error) { - vals := make(map[string]map[interface{}]interface{}) - parsed := make(map[string]map[string]interface{}) - if !utils.Exists(filename) { - return parsed, nil - } - - contents, err := os.ReadFile(filename) - if err != nil { - return parsed, err - } - if err := yaml.Unmarshal(contents, &vals); err != nil { - return parsed, err - } - - for k, v := range vals { - parsed[k] = utils.CleanUpInterfaceMap(v) - } - - return parsed, nil -} - -func (s *Scaffold) createChart(w *wkspace.Workspace) error { - repo := w.Installation.Repository - if len(w.Charts) == 0 { - return utils.HighlightError(fmt.Errorf("No charts installed for this repository. You might need to run `plural bundle install %s `.", repo.Name)) - } - - version := "0.1.0" - filename := pathing.SanitizeFilepath(filepath.Join(s.Root, ChartfileName)) - - if utils.Exists(filename) { - content, err := os.ReadFile(filename) - if err != nil { - return errors.ErrorWrap(err, "Failed to read existing Chart.yaml") - } - - chart := chart{} - if err := yaml.Unmarshal(content, &chart); err != nil { - return errors.ErrorWrap(err, "Existing Chart.yaml has invalid yaml formatting") - } - - version = chart.Version - } - - appVersion := appVersion(w.Charts) - chart := &chart{ - ApiVersion: "v2", - Name: repo.Name, - Description: fmt.Sprintf("A helm chart for %s", repo.Name), - Version: version, - AppVersion: appVersion, - Dependencies: s.chartDependencies(w), - } - - chartFile, err := yaml.Marshal(chart) - if err != nil { - return err - } - - if err := utils.WriteFile(filename, chartFile); err != nil { - return err - } - - files := []struct { - path string - content []byte - force bool - }{ - { - // .helmignore - path: pathing.SanitizeFilepath(filepath.Join(s.Root, IgnorefileName)), - content: []byte(defaultIgnore), - }, - { - // NOTES.txt - path: pathing.SanitizeFilepath(filepath.Join(s.Root, NotesName)), - content: []byte(defaultNotes), - force: true, - }, - { - // templates/secret.yaml - path: pathing.SanitizeFilepath(filepath.Join(s.Root, LicenseSecretName)), - content: []byte(licenseSecret), - force: true, - }, - { - // templates/licnse.yaml - path: pathing.SanitizeFilepath(filepath.Join(s.Root, LicenseCrdName)), - content: []byte(fmt.Sprintf(license, repo.Name)), - force: true, - }, - } - - for _, file := range files { - if !file.force { - if _, err := os.Stat(file.path); err == nil { - // File exists and is okay. Skip it. - continue - } - } - if err := utils.WriteFile(file.path, file.content); err != nil { - return err - } - } - - // remove old requirements.yaml files to fully migrate to helm v3 - reqsFile := pathing.SanitizeFilepath(filepath.Join(s.Root, "requirements.yaml")) - if utils.Exists(reqsFile) { - if err := os.Remove(reqsFile); err != nil { - return err - } - } - - tpl, err := ttpl.New("gotpl").Parse(defaultApplication) - if err != nil { - return err - } - - var appBuffer bytes.Buffer - vars := map[string]string{ - "Name": repo.Name, - "Version": appVersion, - "Description": repo.Description, - "Icon": repo.Icon, - "DarkIcon": repo.DarkIcon, - } - if err := tpl.Execute(&appBuffer, vars); err != nil { - return err - } - appBuffer.WriteString(appTemplate) - - if err := utils.WriteFile(pathing.SanitizeFilepath(filepath.Join(s.Root, ApplicationName)), appBuffer.Bytes()); err != nil { - return err - } - - // Need to add the ChartsDir explicitly as it does not contain any file OOTB - if err := os.MkdirAll(pathing.SanitizeFilepath(filepath.Join(s.Root, ChartsDir)), 0755); err != nil { - return err - } - - return nil -} - -func repoUrl(w *wkspace.Workspace, repo string, chart string) string { - if w.Links != nil { - if path, ok := w.Links.Helm[chart]; ok { - return fmt.Sprintf("file://%s", path) - } - } - url := strings.ReplaceAll(w.Config.BaseUrl(), "https", "cm") - return fmt.Sprintf("%s/cm/%s", url, repo) -} - -func appVersion(charts []*api.ChartInstallation) string { - for _, inst := range charts { - if inst.Chart.Dependencies.Application { - if inst.Version.Helm != nil { - if vsn, ok := inst.Version.Helm["appVersion"]; ok { - if v, ok := vsn.(string); ok { - return v - } - } - } - return inst.Version.Version - } - } - - return "0.1.0" -} diff --git a/pkg/scaffold/helm_test.go b/pkg/scaffold/helm_test.go deleted file mode 100644 index 3ba426b9d..000000000 --- a/pkg/scaffold/helm_test.go +++ /dev/null @@ -1,214 +0,0 @@ -package scaffold - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v2" - - "github.com/pluralsh/plural-cli/pkg/api" - "github.com/pluralsh/plural-cli/pkg/config" - "github.com/pluralsh/plural-cli/pkg/manifest" - "github.com/pluralsh/plural-cli/pkg/provider" - pluraltest "github.com/pluralsh/plural-cli/pkg/test" - "github.com/pluralsh/plural-cli/pkg/utils/git" - "github.com/pluralsh/plural-cli/pkg/wkspace" -) - -func TestBuildChartValues(t *testing.T) { - tests := []struct { - name string - workspace *wkspace.Workspace - existingValues string - expectedDefaultValues string - expectedValues string - man *manifest.ProjectManifest - expectError bool - }{ - { - name: `test build values first time`, - expectedDefaultValues: `plrl: - license: abc -test: - extraEnv: - - name: ARM_USE_MSI - value: "true" -`, - expectedValues: "", - man: &manifest.ProjectManifest{ - Cluster: "test", - Bucket: "test", - Project: "test", - Provider: "kind", - Region: "test", - }, - workspace: &wkspace.Workspace{ - Installation: &api.Installation{ - Id: "123", - Repository: &api.Repository{ - Name: "test", - }, - LicenseKey: "abc", - }, - Charts: []*api.ChartInstallation{ - { - Id: "123", - Chart: &api.Chart{ - Id: "123", - Name: "test", - }, - Version: &api.Version{ - ValuesTemplate: `output = { - extraEnv={ - { - name="ARM_USE_MSI", - value = 'true' - - }, - } -}`, - TemplateType: "LUA", - }, - }, - }, - Context: &manifest.Context{ - Configuration: map[string]map[string]interface{}{ - "test": {}, - }, - }, - }, - }, - { - name: `test build values when values.yaml exists, add and override variables`, - expectedDefaultValues: `plrl: - license: abc -test: - extraEnv: - - name: ARM_USE_MSI - value: "true" -`, - expectedValues: `plrl: - license: abc -test: - enabled: false - extraEnv: - - name: TEST - value: "false" -`, - existingValues: `plrl: - license: abc -test: - enabled: false - extraEnv: - - name: TEST - value: "false" -`, - man: &manifest.ProjectManifest{ - Cluster: "test", - Bucket: "test", - Project: "test", - Provider: "kind", - Region: "test", - }, - workspace: &wkspace.Workspace{ - Installation: &api.Installation{ - Id: "123", - Repository: &api.Repository{ - Name: "test", - }, - LicenseKey: "abc", - }, - Charts: []*api.ChartInstallation{ - { - Id: "123", - Chart: &api.Chart{ - Id: "123", - Name: "test", - }, - Version: &api.Version{ - ValuesTemplate: `output = { - extraEnv={ - { - name="ARM_USE_MSI", - value = 'true' - - }, - } -}`, - TemplateType: "LUA", - }, - }, - }, - Context: &manifest.Context{ - Configuration: map[string]map[string]interface{}{ - "test": {}, - }, - }, - }, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - dir, err := os.MkdirTemp("", "config") - assert.NoError(t, err) - defer os.RemoveAll(dir) - - os.Setenv("HOME", dir) - defer os.Unsetenv("HOME") - - err = os.Chdir(dir) - assert.NoError(t, err) - - data, err := yaml.Marshal(test.man) - assert.NoError(t, err) - err = os.WriteFile("workspace.yaml", data, os.FileMode(0755)) - assert.NoError(t, err) - - err = os.WriteFile("values.yaml", []byte(test.existingValues), os.FileMode(0755)) - assert.NoError(t, err) - - defaultConfig := pluraltest.GenDefaultConfig() - err = defaultConfig.Save(config.ConfigName) - assert.NoError(t, err) - - _, err = git.Init() - assert.NoError(t, err) - _, err = git.GitRaw("config", "--global", "user.email", "test@plural.com") - assert.NoError(t, err) - _, err = git.GitRaw("config", "--global", "user.name", "test") - assert.NoError(t, err) - _, err = git.GitRaw("add", "-A") - assert.NoError(t, err) - _, err = git.GitRaw("commit", "-m", "init") - assert.NoError(t, err) - _, err = git.GitRaw("remote", "add", "origin", "git@git.test.com:portfolio/space.space_name.git") - assert.NoError(t, err) - - provider, err := provider.FromManifest(test.man) - if err != nil { - t.Fatal(err) - } - test.workspace.Provider = provider - scaffold := Scaffold{ - Name: "test", - Root: dir, - } - - err = scaffold.buildChartValues(test.workspace) - if test.expectError { - assert.Error(t, err) - return - } - assert.NoError(t, err) - defaultValues, err := os.ReadFile(filepath.Join(dir, "default-values.yaml")) - assert.NoError(t, err) - assert.Equal(t, test.expectedDefaultValues, string(defaultValues)) - - values, err := os.ReadFile(filepath.Join(dir, "values.yaml")) - assert.NoError(t, err) - assert.Equal(t, test.expectedValues, string(values)) - }) - } -} diff --git a/pkg/scaffold/readme.go b/pkg/scaffold/readme.go deleted file mode 100644 index e16ede9a3..000000000 --- a/pkg/scaffold/readme.go +++ /dev/null @@ -1,186 +0,0 @@ -package scaffold - -import ( - "fmt" - "os" - "path" - "path/filepath" - "reflect" - "regexp" - "runtime" - "strings" - "sync" - - log "github.com/sirupsen/logrus" - - "github.com/norwoodj/helm-docs/pkg/document" - "github.com/norwoodj/helm-docs/pkg/helm" - "github.com/spf13/viper" -) - -// parallelProcessIterable runs the visitFn function on each element of the iterable, using -// parallelism number of worker goroutines. The iterable may be a slice or a map. In the case of a -// map, the argument passed to visitFn will be the key. -func parallelProcessIterable(iterable interface{}, parallelism int, visitFn func(elem interface{})) { - workChan := make(chan interface{}) - - wg := &sync.WaitGroup{} - wg.Add(parallelism) - - for i := 0; i < parallelism; i++ { - go func() { - defer wg.Done() - for elem := range workChan { - visitFn(elem) - } - }() - } - - iterableValue := reflect.ValueOf(iterable) - - if iterableValue.Kind() == reflect.Map { - for _, key := range iterableValue.MapKeys() { - workChan <- key.Interface() - } - } else { - sliceLen := iterableValue.Len() - for i := 0; i < sliceLen; i++ { - workChan <- iterableValue.Index(i).Interface() - } - } - - close(workChan) - wg.Wait() -} - -func getDocumentationParsingConfigFromArgs() (helm.ChartValuesDocumentationParsingConfig, error) { - var regexps []*regexp.Regexp - regexpStrings := []string{".*service\\.type", ".*image\\.repository", ".*image\\.tag"} - for _, item := range regexpStrings { - regex, err := regexp.Compile(item) - if err != nil { - return helm.ChartValuesDocumentationParsingConfig{}, err - } - regexps = append(regexps, regex) - } - return helm.ChartValuesDocumentationParsingConfig{ - StrictMode: false, - AllowedMissingValuePaths: []string{}, - AllowedMissingValueRegexps: regexps, - }, nil -} - -func readDocumentationInfoByChartPath(chartSearchRoot string, parallelism int) (map[string]helm.ChartDocumentationInfo, error) { - var fullChartSearchRoot string - - if path.IsAbs(chartSearchRoot) { - fullChartSearchRoot = chartSearchRoot - } else { - cwd, err := os.Getwd() - if err != nil { - return nil, fmt.Errorf("error getting working directory: %w", err) - } - - fullChartSearchRoot = filepath.Join(cwd, chartSearchRoot) - } - - chartDirs, err := helm.FindChartDirectories(fullChartSearchRoot) - if err != nil { - return nil, fmt.Errorf("error finding chart directories: %w", err) - } - - log.Infof("Found Chart directories [%s]", strings.Join(chartDirs, ", ")) - - templateFiles := []string{"README.md.gotmpl"} - log.Debugf("Rendering from optional template files [%s]", strings.Join(templateFiles, ", ")) - - documentationInfoByChartPath := make(map[string]helm.ChartDocumentationInfo, len(chartDirs)) - documentationInfoByChartPathMu := &sync.Mutex{} - documentationParsingConfig, err := getDocumentationParsingConfigFromArgs() - if err != nil { - return nil, fmt.Errorf("error parsing the linting config%w", err) - } - - parallelProcessIterable(chartDirs, parallelism, func(elem interface{}) { - chartDir := elem.(string) - info, err := helm.ParseChartInformation(filepath.Join(chartSearchRoot, chartDir), documentationParsingConfig) - if err != nil { - log.Warnf("Error parsing information for chart %s, skipping: %s", chartDir, err) - return - } - documentationInfoByChartPathMu.Lock() - documentationInfoByChartPath[info.ChartDirectory] = info - documentationInfoByChartPathMu.Unlock() - }) - - return documentationInfoByChartPath, nil -} - -func getChartToGenerate(documentationInfoByChartPath map[string]helm.ChartDocumentationInfo) map[string]helm.ChartDocumentationInfo { - generateDirectories := []string{} - if len(generateDirectories) == 0 { - return documentationInfoByChartPath - } - documentationInfoToGenerate := make(map[string]helm.ChartDocumentationInfo, len(generateDirectories)) - var skipped = false - for _, chartDirectory := range generateDirectories { - if info, ok := documentationInfoByChartPath[chartDirectory]; ok { - documentationInfoToGenerate[chartDirectory] = info - } else { - log.Warnf("Couldn't find documentation Info for <%s> - skipping", chartDirectory) - skipped = true - } - } - if skipped { - possibleCharts := []string{} - for path := range documentationInfoByChartPath { - possibleCharts = append(possibleCharts, path) - } - log.Warnf("Some charts listed in `chart-to-generate` wasn't found. List of charts to choose: [%s]", strings.Join(possibleCharts, ", ")) - } - return documentationInfoToGenerate -} - -func writeDocumentation(chartSearchRoot string, documentationInfoByChartPath map[string]helm.ChartDocumentationInfo, dryRun bool, parallelism int) { - templateFiles := []string{"README.md.gotmpl"} - badgeStyle := "flat-square" - - log.Debugf("Rendering from optional template files [%s]", strings.Join(templateFiles, ", ")) - - documentDependencyValues := true - documentationInfoToGenerate := getChartToGenerate(documentationInfoByChartPath) - - parallelProcessIterable(documentationInfoToGenerate, parallelism, func(elem interface{}) { - info := documentationInfoByChartPath[elem.(string)] - var err error - var dependencyValues []document.DependencyValues - if documentDependencyValues { - dependencyValues, err = document.GetDependencyValues(info, documentationInfoByChartPath) - if err != nil { - log.Warnf("Error evaluating dependency values for chart %s, skipping: %v", info.ChartDirectory, err) - return - } - } - document.PrintDocumentation(info, chartSearchRoot, templateFiles, dryRun, "v1.11.0", badgeStyle, dependencyValues) - }) -} - -func Readme(chartSearchRoot string, dryRun bool) error { - parallelism := runtime.NumCPU() * 2 - log.SetLevel(log.FatalLevel) - viper.Set("values-file", "values.yaml") - viper.Set("output-file", "README.md") - - // On dry runs all output goes to stdout, and so as to not jumble things, generate serially. - if dryRun { - parallelism = 1 - } - - documentationInfoByChartPath, err := readDocumentationInfoByChartPath(chartSearchRoot, parallelism) - if err != nil { - return err - } - - writeDocumentation(chartSearchRoot, documentationInfoByChartPath, dryRun, parallelism) - return nil -} diff --git a/pkg/scaffold/scaffold.go b/pkg/scaffold/scaffold.go deleted file mode 100644 index 287f0addc..000000000 --- a/pkg/scaffold/scaffold.go +++ /dev/null @@ -1,166 +0,0 @@ -package scaffold - -import ( - "os" - "path/filepath" - - "github.com/pluralsh/plural-cli/pkg/executor" - "github.com/pluralsh/plural-cli/pkg/utils/git" - "github.com/pluralsh/plural-cli/pkg/utils/pathing" - "github.com/pluralsh/plural-cli/pkg/wkspace" - "github.com/pluralsh/polly/algorithms" - "github.com/pluralsh/polly/containers" - "github.com/rodaine/hclencoder" -) - -type Scaffold struct { - Name string `hcl:",key"` - Path string `hcl:"path"` - Type string `hcl:"type"` - Root string `hcle:"omit"` - - Preflight []*executor.Step `hcl:"preflight"` -} - -type Metadata struct { - Name string `hcl:"name"` -} - -type Build struct { - Metadata *Metadata `hcl:"metadata"` - Scaffolds []*Scaffold `hcl:"scaffold"` -} - -const ( - TF = "terraform" - HELM = "helm" - CRD = "crd" -) - -func Scaffolds(wk *wkspace.Workspace) (*Build, error) { - repoRoot, err := git.Root() - if err != nil { - return &Build{}, err - } - - name := wk.Installation.Repository.Name - wkspaceRoot := pathing.SanitizeFilepath(filepath.Join(repoRoot, name)) - - build, err := Read(wkspaceRoot) - def := Default(wk, name) - if err != nil { - return def, nil - } - - return merge(build, def), nil -} - -func merge(build *Build, base *Build) *Build { - byName := make(map[string]*Scaffold) - for _, scaffold := range build.Scaffolds { - byName[scaffold.Name] = scaffold - } - for _, scaffold := range base.Scaffolds { - if prev, ok := byName[scaffold.Name]; ok { - mergePreflights(scaffold, prev) - } - byName[scaffold.Name] = scaffold - } - - // to handle helm v3 transition - delete(byName, "add-repo") - - graph := containers.NewGraph[string]() - for i := 0; i < len(build.Scaffolds)-1; i++ { - graph.AddEdge(build.Scaffolds[i].Name, build.Scaffolds[i+1].Name) - } - - for i := 0; i < len(base.Scaffolds)-1; i++ { - graph.AddEdge(base.Scaffolds[i].Name, base.Scaffolds[i+1].Name) - } - - sorted, _ := algorithms.TopsortGraph(graph) - build.Scaffolds = algorithms.Map(sorted, func(n string) *Scaffold { return byName[n] }) - return build -} - -func mergePreflights(new, old *Scaffold) { - byName := make(map[string]*executor.Step) - for _, preflight := range old.Preflight { - byName[preflight.Name] = preflight - } - - for _, preflight := range new.Preflight { - if prev, ok := byName[preflight.Name]; ok { - preflight.Sha = prev.Sha - } - } -} - -func (b *Build) Flush(root string) error { - io, err := hclencoder.Encode(&b) - if err != nil { - return err - } - - path, _ := filepath.Abs(pathing.SanitizeFilepath(filepath.Join(root, b.Metadata.Name, "build.hcl"))) - return os.WriteFile(path, io, 0644) -} - -func (s *Scaffold) Execute(wk *wkspace.Workspace, force bool) error { - os.Setenv("HELM_REPO_ACCESS_TOKEN", wk.Config.Token) - err := s.executeType(wk) - if err != nil { - return err - } - - ignore := []string{} - for _, preflight := range s.Preflight { - if force { - preflight.Sha = "" - } - - sha, err := preflight.Execute(s.Root, ignore) - if err != nil { - return err - } - preflight.Sha = sha - } - - return nil -} - -func (s *Scaffold) executeType(wk *wkspace.Workspace) error { - switch s.Type { - case TF: - return s.handleTerraform(wk) - case HELM: - return s.handleHelm(wk) - case CRD: - return s.buildCrds(wk) - default: - return nil - } -} - -func (b *Build) Execute(wk *wkspace.Workspace, force bool) error { - root, err := git.Root() - if err != nil { - return err - } - - for _, s := range b.Scaffolds { - path := pathing.SanitizeFilepath(filepath.Join(root, b.Metadata.Name, s.Path)) - if err := os.MkdirAll(path, os.ModePerm); err != nil { - b.Flush(root) - return err - } - s.Root = path - if err := s.Execute(wk, force); err != nil { - b.Flush(root) - return err - } - } - - return b.Flush(root) -} diff --git a/pkg/scaffold/secrets.go b/pkg/scaffold/secrets.go deleted file mode 100644 index 6837ec540..000000000 --- a/pkg/scaffold/secrets.go +++ /dev/null @@ -1,20 +0,0 @@ -package scaffold - -import ( - "bytes" - "fmt" - - "github.com/pluralsh/plural-cli/pkg/utils" -) - -const filterTmpl = "%s filter=plural-crypt diff=plural-crypt\n" - -func buildSecrets(file string, secrets []string) error { - var b bytes.Buffer - b.Grow(32) - for _, secret := range secrets { - fmt.Fprintf(&b, filterTmpl, secret) - } - - return utils.WriteFile(file, b.Bytes()) -} diff --git a/pkg/scaffold/template/template.go b/pkg/scaffold/template/template.go index f59f65de1..1e5098cd1 100644 --- a/pkg/scaffold/template/template.go +++ b/pkg/scaffold/template/template.go @@ -36,46 +36,6 @@ func templateInfo(path string) (t gqlclient.TemplateType, contents string, err e return } -func BuildValuesFromTemplate(vals map[string]interface{}, w *wkspace.Workspace) (map[string]map[string]interface{}, error) { - globals := map[string]interface{}{} - output := make(map[string]map[string]interface{}) - for _, chartInst := range w.Charts { - chartName := chartInst.Chart.Name - tplate := chartInst.Version.ValuesTemplate - templateType := chartInst.Version.TemplateType - - if w.Links != nil { - if path, ok := w.Links.Helm[chartName]; ok { - var err error - templateType, tplate, err = templateInfo(path) - if err != nil { - return nil, err - } - } - } - - switch templateType { - case gqlclient.TemplateTypeGotemplate: - if err := FromGoTemplate(vals, globals, output, chartName, tplate); err != nil { - return nil, err - } - case gqlclient.TemplateTypeLua: - if err := FromLuaTemplate(vals, globals, output, chartName, tplate); err != nil { - return nil, err - } - } - } - - if len(globals) > 0 { - output["global"] = globals - } - - output["plrl"] = map[string]interface{}{ - "license": w.Installation.LicenseKey, - } - return output, nil -} - func TmpValuesFile(path string) (f *os.File, err error) { conf := config.Read() if strings.HasSuffix(path, "lua") { diff --git a/pkg/scaffold/terraform.go b/pkg/scaffold/terraform.go deleted file mode 100644 index 1efbff6d6..000000000 --- a/pkg/scaffold/terraform.go +++ /dev/null @@ -1,283 +0,0 @@ -package scaffold - -import ( - "bytes" - "fmt" - "net/http" - "os" - "path/filepath" - "regexp" - "sort" - "strings" - - "golang.org/x/exp/maps" - - "golang.org/x/mod/semver" - - "github.com/pluralsh/plural-cli/pkg/api" - "github.com/pluralsh/plural-cli/pkg/config" - "github.com/pluralsh/plural-cli/pkg/template" - "github.com/pluralsh/plural-cli/pkg/utils" - "github.com/pluralsh/plural-cli/pkg/utils/pathing" - "github.com/pluralsh/plural-cli/pkg/wkspace" -) - -const moduleTemplate = `module "{{ .Values.name }}" { - source = "{{ .Values.path }}" - -### BEGIN MANUAL SECTION <<{{ .Values.name }}>> -{{ .Values.Manual }} -### END MANUAL SECTION <<{{ .Values.name }}>> - -{{ .Values.conf | nindent 2 }} -{{ range $key, $val := .Values.deps }} - {{ $key }} = module.{{ $val }} -{{- end }} -} -` - -const outputTemplate = `output "{{ .Name }}" { - value = module.{{ .Module }}.{{ .Value }} - sensitive = true -} -` - -const helpDoc = ` -############## Helpful Hints ################### - -# Any configuration updates should be done in this file in a manual section. You can also create files alongside this one, -# and plural build will preserve them. That's the ideal strategy if you want to add additional resources like a sql instance -# or vpn gateway. - -# The submodule folders within this directory are generated by plural build and we cannot guarantee a change in -# them will be merged properly. - -################################################# - -` - -func (scaffold *Scaffold) handleTerraform(wk *wkspace.Workspace) error { - repo := wk.Installation.Repository - providerCtx := buildContext(wk, repo.Name, wk.Terraform) - - var providerVersions semver.ByVersion - - if len(wk.Terraform) == 0 { - return nil - } - - for i := range wk.Terraform { - providerVersions = append(providerVersions, wk.Terraform[i].Terraform.Dependencies.ProviderVsn) - } - - semver.Sort(providerVersions) - - // use the latest version of the TF template for the provider - backend, err := wk.Provider.CreateBackend(repo.Name, providerVersions[providerVersions.Len()-1], providerCtx) - if err != nil { - return err - } - - apps, err := NewApplications() - if err != nil { - return err - } - - if err := scaffold.untarModules(wk); err != nil { - return err - } - - mainFile := pathing.SanitizeFilepath(filepath.Join(scaffold.Root, "main.tf")) - contents, err := utils.ReadFile(mainFile) - if err != nil { - contents = "" - } - - var modules = make([]string, len(wk.Terraform)+1) - modules[0] = helpDoc + backend - ctx, _ := wk.Context.Repo(repo.Name) - links := wk.Links - for i, tfInst := range wk.Terraform { - tf := tfInst.Terraform - linkPath := "" - if links != nil { - if path, ok := links.Terraform[tf.Name]; ok { - linkPath = path - } - } - - var buf bytes.Buffer - buf.Grow(5 * 1024) - plate := tfInst.Version.ValuesTemplate - if linkPath != "" { - var err error - plate, err = utils.ReadFile(pathing.SanitizeFilepath(filepath.Join(linkPath, "terraform.tfvars"))) - if err != nil { - return err - } - } - - tmpl, err := template.MakeTemplate(plate) - if err != nil { - return err - } - values := map[string]interface{}{ - "Values": ctx, - "Configuration": wk.Context.Configuration, - "Cluster": wk.Provider.Cluster(), - "Project": wk.Provider.Project(), - "Namespace": wk.Config.Namespace(repo.Name), - "Region": wk.Provider.Region(), - "Context": wk.Provider.Context(), - "Config": config.Read(), - "Applications": apps, - } - if err := tmpl.Execute(&buf, values); err != nil { - return err - } - - module := make(map[string]interface{}) - module["name"] = tf.Name - if linkPath != "" { - module["path"] = linkPath - } else { - module["path"] = "./" + tf.Name - } - - module["conf"] = buf.String() - if tf.Dependencies != nil && tf.Dependencies.Wirings != nil { - module["deps"] = tf.Dependencies.Wirings.Terraform - } else { - module["deps"] = map[string]interface{}{} - } - module["Manual"] = manualSection(contents, tf.Name) - - var moduleBuf bytes.Buffer - moduleBuf.Grow(1024) - if err := template.RenderTemplate(&moduleBuf, moduleTemplate, module); err != nil { - return err - } - - modules[i+1] = moduleBuf.String() - - valuesFile := pathing.SanitizeFilepath(filepath.Join(scaffold.Root, tf.Name, "terraform.tfvars")) - os.Remove(valuesFile) - - moduleBuf.Reset() - buf.Reset() - } - - if err := utils.WriteFile(mainFile, []byte(strings.Join(modules, "\n\n"))); err != nil { - return err - } - - if err := scaffold.buildOutputs(wk); err != nil { - return err - } - - secrets := buildTfSecrets(wk.Terraform) - if err := buildSecrets(pathing.SanitizeFilepath(filepath.Join(scaffold.Root, ".gitattributes")), secrets); err != nil { - return err - } - - return nil -} - -// TODO: move to some sort of scaffold util? -func (scaffold *Scaffold) untarModules(wk *wkspace.Workspace) error { - length := len(wk.Terraform) - utils.Highlight("unpacking %d %s", len(wk.Terraform), utils.Pluralize("module", "modules", length)) - for _, tfInst := range wk.Terraform { - tf := tfInst.Terraform - v := tfInst.Version - path := pathing.SanitizeFilepath(filepath.Join(scaffold.Root, tf.Name)) - if err := os.MkdirAll(path, os.ModePerm); err != nil { - fmt.Print("\n") - return err - } - - if err := untar(v, tf, path); err != nil { - fmt.Print("\n") - return err - } - fmt.Print(".") - } - - utils.Success("\u2713\n") - return nil -} - -func (scaffold *Scaffold) buildOutputs(wk *wkspace.Workspace) error { - var buf bytes.Buffer - buf.Grow(5 * 1024) - - tmp, err := template.MakeTemplate(outputTemplate) - if err != nil { - return err - } - - sort.SliceStable(wk.Terraform, func(i, j int) bool { - return wk.Terraform[i].Terraform.Name < wk.Terraform[j].Terraform.Name - }) - for _, tfInst := range wk.Terraform { - tfName := tfInst.Terraform.Name - outs := tfInst.Version.Dependencies.Outputs - outputs := maps.Keys(outs) - sort.Strings(outputs) - for _, name := range outputs { - value := outs[name] - err = tmp.Execute(&buf, map[string]interface{}{"Name": name, "Value": value, "Module": tfName}) - if err != nil { - return err - } - buf.WriteString("\n\n") - } - } - - outputFile := pathing.SanitizeFilepath(filepath.Join(scaffold.Root, "outputs.tf")) - return utils.WriteFile(outputFile, buf.Bytes()) -} - -func untar(v *api.Version, tf *api.Terraform, dir string) error { - resp, err := http.Get(v.Package) - if err != nil { - return err - } - - return utils.Untar(resp.Body, dir, tf.Name) -} - -func manualSection(contents, name string) string { - re := regexp.MustCompile(fmt.Sprintf(`(?s)### BEGIN MANUAL SECTION <<%s>>(.*)### END MANUAL SECTION <<%s>>`, name, name)) - matches := re.FindStringSubmatch(contents) - if len(matches) > 0 { - return strings.TrimSpace(matches[1]) - } - - return "" -} - -func buildTfSecrets(installations []*api.TerraformInstallation) []string { - res := []string{} - for _, inst := range installations { - res = append(res, inst.Version.Dependencies.Secrets...) - } - return res -} - -func buildContext(wk *wkspace.Workspace, repo string, installations []*api.TerraformInstallation) map[string]interface{} { - ctx := map[string]interface{}{ - "Namespace": wk.Config.Namespace(repo), - } - - for _, inst := range installations { - for k, v := range inst.Version.Dependencies.ProviderWirings { - if k == "cluster" { - ctx["Cluster"] = v - } - ctx[k] = v - } - } - - return ctx -} diff --git a/pkg/wkspace/actions.go b/pkg/wkspace/actions.go deleted file mode 100644 index 040238f37..000000000 --- a/pkg/wkspace/actions.go +++ /dev/null @@ -1,172 +0,0 @@ -package wkspace - -import ( - "fmt" - "os" - "path/filepath" - "strings" - "time" - - "go.mercari.io/hcledit" - "helm.sh/helm/v3/pkg/action" - - "github.com/pluralsh/plural-cli/pkg/executor" - "github.com/pluralsh/plural-cli/pkg/helm" - "github.com/pluralsh/plural-cli/pkg/kubernetes" - "github.com/pluralsh/plural-cli/pkg/utils" - "github.com/pluralsh/plural-cli/pkg/utils/git" - "github.com/pluralsh/plural-cli/pkg/utils/pathing" -) - -type checker func(s string) bool - -var alwaysErr checker = func(s string) bool { return false } - -func (c checker) execSuppressed(command string, args ...string) (err error) { - for retry := 2; retry >= 0; retry-- { - utils.Highlight("%s %s ~> ", command, strings.Join(args, " ")) - cmd, out := executor.SuppressedCommand(command, args...) - err = executor.RunCommand(cmd, out) - if err == nil || c(out.Format()) { - break - } - fmt.Printf("retrying command, number of retries remaining: %d\n", retry) - } - - return -} - -func (w *Workspace) DestroyHelm() error { - // ensure current kubeconfig is correct before destroying stuff - if err := w.Provider.KubeConfig(); err != nil { - return err - } - - name := w.Installation.Repository.Name - namespace := w.Config.Namespace(name) - var err error - for retry := 2; retry >= 0; retry-- { - err = uninstallHelm(name, namespace) - if err == nil { - break - } - fmt.Printf("retrying command, number of retries remaining: %d\n", retry) - } - - return err - -} - -func (w *Workspace) Bounce() error { - return w.ToMinimal().BounceHelm(false, nil, nil, nil) -} - -func (w *Workspace) HelmDiff() error { - return w.ToMinimal().DiffHelm() -} - -func (w *Workspace) Destroy() error { - if err := w.DestroyHelm(); err != nil { - return err - } - - if err := w.DestroyTerraform(); err != nil { - return err - } - - return w.Reset() -} - -func (w *Workspace) Reset() error { - repo := w.Installation.Repository - repoRoot, err := git.Root() - if err != nil { - return err - } - - deployFile := pathing.SanitizeFilepath(filepath.Join(repoRoot, repo.Name, "deploy.hcl")) - editor, err := hcledit.ReadFile(deployFile) - if err != nil { - return err - } - if err := editor.Update("step.*.sha", ""); err != nil { - return err - } - if err := editor.OverWriteFile(); err != nil { - return err - } - - return nil -} - -func (w *Workspace) DestroyTerraform() error { - repo := w.Installation.Repository - repoRoot, err := git.Root() - if err != nil { - return err - } - path, err := filepath.Abs(pathing.SanitizeFilepath(filepath.Join(repoRoot, repo.Name, "terraform"))) - if err != nil { - return err - } - - time.AfterFunc(1*time.Minute, func() { - kube, err := kubernetes.Kubernetes() - if err != nil { - fmt.Printf("Could not set up k8s client due to %s\n", err) - return - } - - ns := w.Config.Namespace(repo.Name) - if err := kube.FinalizeNamespace(ns); err != nil { - return - } - }) - - if err := os.Chdir(path); err != nil { - return err - } - if err := alwaysErr.execSuppressed("terraform", "init", "-upgrade"); err != nil { - return err - } - - return alwaysErr.execSuppressed("terraform", "destroy", "-auto-approve") -} - -func uninstallHelm(name, namespace string) error { - exists, err := isReleaseAvailable(name, namespace) - if err != nil { - return err - } - if exists { - actionConfig, err := helm.GetActionConfig(namespace) - if err != nil { - return err - } - client := action.NewUninstall(actionConfig) - - _, err = client.Run(name) - if err != nil { - return err - } - } - return nil -} - -func isReleaseAvailable(name, namespace string) (bool, error) { - actionConfig, err := helm.GetActionConfig(namespace) - if err != nil { - return false, err - } - client := action.NewList(actionConfig) - resp, err := client.Run() - if err != nil { - return false, err - } - for _, rel := range resp { - if rel.Name == name { - return true, nil - } - } - return false, nil -} diff --git a/pkg/wkspace/builder.go b/pkg/wkspace/builder.go index 417dbce3e..4e8b00afc 100644 --- a/pkg/wkspace/builder.go +++ b/pkg/wkspace/builder.go @@ -1,273 +1,13 @@ package wkspace import ( - "fmt" - "os" - "path/filepath" - "strings" - - "golang.org/x/mod/semver" - - "github.com/pluralsh/plural-cli/pkg/api" "github.com/pluralsh/plural-cli/pkg/config" - "github.com/pluralsh/plural-cli/pkg/crypto" - "github.com/pluralsh/plural-cli/pkg/diff" - "github.com/pluralsh/plural-cli/pkg/executor" "github.com/pluralsh/plural-cli/pkg/manifest" "github.com/pluralsh/plural-cli/pkg/provider" - "github.com/pluralsh/plural-cli/pkg/utils" - "github.com/pluralsh/plural-cli/pkg/utils/git" - "github.com/pluralsh/plural-cli/pkg/utils/pathing" - - "github.com/pluralsh/polly/algorithms" ) type Workspace struct { - Provider provider.Provider - Installation *api.Installation - Charts []*api.ChartInstallation - Terraform []*api.TerraformInstallation - Config *config.Config - Manifest *manifest.ProjectManifest - Context *manifest.Context - Links *manifest.Links -} - -func New(client api.Client, inst *api.Installation) (*Workspace, error) { - ci, ti, err := client.GetPackageInstallations(inst.Repository.Id) - if err != nil { - return nil, api.GetErrorResponse(err, "GetPackageInstallations") - } - - 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() - ctx, err := manifest.ReadContext(manifest.ContextPath()) - if err != nil { - return nil, err - } - - manifestPath := manifestPath(inst.Repository.Name) - man, err := manifest.Read(manifestPath) - var links *manifest.Links - if err == nil { - links = man.Links - } - - wk := &Workspace{ - Provider: prov, - Installation: inst, - Charts: ci, - Terraform: ti, - Config: &conf, - Context: ctx, - Manifest: project, - Links: links, - } - return wk, nil -} - -func Configured(repo string) bool { - ctx, err := manifest.ReadContext(manifest.ContextPath()) - if err != nil { - return false - } - - _, ok := ctx.Configuration[repo] - return ok -} - -func (wk *Workspace) PrintLinks() { - if wk.Links == nil { - return - } - - fmt.Printf("\n") - doPrintLinks("helm", wk.Links.Helm) - doPrintLinks("terraform", wk.Links.Terraform) -} - -func (wk *Workspace) RequiredCliVsn() (vsn string, ok bool) { - cVsns := algorithms.Map(wk.Charts, func(c *api.ChartInstallation) string { return c.Version.Dependencies.CliVsn }) - tVsns := algorithms.Map(wk.Terraform, func(t *api.TerraformInstallation) string { return t.Version.Dependencies.CliVsn }) - vsns := algorithms.Filter(append(cVsns, tVsns...), func(v string) bool { return v != "" }) - vsns = algorithms.Map(vsns, func(v string) string { - if strings.HasPrefix(v, "v") { - return v - } - return fmt.Sprintf("v%s", v) - }) - vsns = algorithms.Filter(vsns, semver.IsValid) - if len(vsns) == 0 { - return - } - - semver.Sort(vsns) - return vsns[len(vsns)-1], true -} - -func doPrintLinks(name string, links map[string]string) { - if len(links) == 0 { - return - } - - utils.Highlight("configured %s links:\n", name) - for name, path := range links { - fmt.Printf("\t%s ==> %s\n", name, path) - } - - fmt.Printf("\n") -} - -func (wk *Workspace) ToMinimal() *MinimalWorkspace { - return &MinimalWorkspace{ - Name: wk.Installation.Repository.Name, - Provider: wk.Provider, - Config: wk.Config, - Manifest: wk.Manifest, - } -} - -func (wk *Workspace) Prepare() error { - repo := wk.Installation.Repository - repoRoot, err := git.Root() - if err != nil { - return err - } - - if err := mkdir(pathing.SanitizeFilepath(filepath.Join(repoRoot, repo.Name))); err != nil { - return err - } - - path, _ := manifest.ManifestPath(repo.Name) - prev, err := manifest.Read(path) - if err != nil { - prev = &manifest.Manifest{} - } - - man := wk.BuildManifest(prev) - if err := man.Write(path); err != nil { - return err - } - - if err := wk.buildExecution(repoRoot); err != nil { - return err - } - - if err := wk.buildDiff(repoRoot); err != nil { - return err - } - - return nil -} - -func (wk *Workspace) requiresWait() bool { - for _, ci := range wk.Charts { - if ci.Version.Dependencies.Wait { - return true - } - } - - for _, ti := range wk.Terraform { - if ti.Version.Dependencies.Wait { - return true - } - } - return false -} - -func (wk *Workspace) buildExecution(repoRoot string) error { - name := wk.Installation.Repository.Name - wkspaceRoot := filepath.Join(repoRoot, name) - - if err := mkdir(pathing.SanitizeFilepath(filepath.Join(wkspaceRoot, ".plural"))); err != nil { - return err - } - - onceFile := pathing.SanitizeFilepath(filepath.Join(wkspaceRoot, ".plural", "ONCE")) - if err := os.WriteFile(onceFile, []byte("once"), 0644); err != nil { - return err - } - - nonceFile := pathing.SanitizeFilepath(filepath.Join(wkspaceRoot, ".plural", "NONCE")) - if err := os.WriteFile(nonceFile, []byte(crypto.RandString(32)), 0644); err != nil { - return err - } - - if err := executor.Ignore(wkspaceRoot); err != nil { - return err - } - - exec, _ := executor.GetExecution(pathing.SanitizeFilepath(wkspaceRoot), "deploy") - - return executor.DefaultExecution(name, exec).Flush(repoRoot) -} - -func (wk *Workspace) buildDiff(repoRoot string) error { - name := wk.Installation.Repository.Name - wkspaceRoot := pathing.SanitizeFilepath(filepath.Join(repoRoot, name)) - - d, _ := diff.GetDiff(pathing.SanitizeFilepath(wkspaceRoot), "diff") - - return diff.DefaultDiff(name, d).Flush(repoRoot) -} - -func DiffedRepos() ([]string, error) { - files, err := git.Modified() - repos := make(map[string]bool) - if err != nil { - return nil, err - } - - for _, file := range files { - // we don't want to respect the OS separators here, it is always a forwards slash on git - parts := strings.Split(file, "/") - if len(parts) <= 1 { - continue - } - - maybeRepo := parts[0] - if utils.Exists(manifestPath(maybeRepo)) && file != manifestPath(maybeRepo) { - repos[maybeRepo] = true - } - } - - result := make([]string, len(repos)) - count := 0 - for repo := range repos { - result[count] = repo - count++ - } - - return result, nil -} - -func isRepo(name string) bool { - repoRoot, err := git.Root() - if err != nil { - return false - } - - return utils.Exists(pathing.SanitizeFilepath(filepath.Join(repoRoot, name, "manifest.yaml"))) -} - -func mkdir(path string) error { - if err := os.MkdirAll(path, os.ModePerm); err != nil { - return err - } - return nil -} - -func manifestPath(repo string) string { - path, _ := filepath.Abs(pathing.SanitizeFilepath(filepath.Join(repo, "manifest.yaml"))) - return path + Provider provider.Provider + Config *config.Config + Context *manifest.Context } diff --git a/pkg/wkspace/builder_test.go b/pkg/wkspace/builder_test.go deleted file mode 100644 index 44f79719a..000000000 --- a/pkg/wkspace/builder_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package wkspace - -import ( - "testing" - - "github.com/pluralsh/plural-cli/pkg/api" - "github.com/stretchr/testify/assert" -) - -func TestRequiredCliVersion(t *testing.T) { - w := &Workspace{ - Charts: []*api.ChartInstallation{ - { - Version: &api.Version{ - Dependencies: &api.Dependencies{ - CliVsn: "0.1.0", - }, - }, - }, - }, - Terraform: []*api.TerraformInstallation{ - { - Version: &api.Version{ - Dependencies: &api.Dependencies{ - CliVsn: "0.2.0", - }, - }, - }, - }, - } - vsn, ok := w.RequiredCliVsn() - assert.True(t, ok) - assert.Equal(t, "v0.2.0", vsn) -} - -func TestRequiredCliVersionEmpty(t *testing.T) { - w := &Workspace{ - Charts: []*api.ChartInstallation{ - { - Version: &api.Version{ - Dependencies: &api.Dependencies{ - CliVsn: "bogus", - }, - }, - }, - }, - Terraform: []*api.TerraformInstallation{ - { - Version: &api.Version{ - Dependencies: &api.Dependencies{ - CliVsn: "", - }, - }, - }, - }, - } - _, ok := w.RequiredCliVsn() - assert.False(t, ok) -} diff --git a/pkg/wkspace/graph.go b/pkg/wkspace/graph.go deleted file mode 100644 index 20af16f73..000000000 --- a/pkg/wkspace/graph.go +++ /dev/null @@ -1,113 +0,0 @@ -package wkspace - -import ( - "github.com/pluralsh/plural-cli/pkg/api" - "github.com/pluralsh/plural-cli/pkg/manifest" - "github.com/pluralsh/polly/algorithms" - "github.com/pluralsh/polly/containers" - "github.com/samber/lo" -) - -func SortAndFilter(installations []*api.Installation) ([]string, error) { - names := lo.FilterMap(installations, func(i *api.Installation, ind int) (string, bool) { - name := i.Repository.Name - return name, isRepo(name) - }) - return TopSortNames(names) -} - -func TopSort(client api.Client, installations []*api.Installation) ([]*api.Installation, error) { - repoMap := map[string]*api.Installation{} - g := containers.NewGraph[string]() - for _, installation := range installations { - repo := installation.Repository.Name - repoMap[repo] = installation - g.AddNode(repo) - - ci, tf, err := client.GetPackageInstallations(installation.Repository.Id) - if err != nil { - return nil, api.GetErrorResponse(err, "GetPackageInstallations") - } - deps := algorithms.Map(buildDependencies(repo, ci, tf), func(d *manifest.Dependency) string { return d.Repo }) - for _, d := range deps { - g.AddEdge(d, repo) - } - } - - names, err := algorithms.TopsortGraph(g) - if err != nil { - return nil, err - } - - insts := lo.FilterMap(names, func(r string, ind int) (i *api.Installation, ok bool) { - i, ok = repoMap[r] - return - }) - return insts, nil -} - -func TopSortNames(repos []string) ([]string, error) { - g := containers.NewGraph[string]() - for _, r := range repos { - g.AddNode(r) - deps, err := findDependencies(r) - if err != nil { - return nil, err - } - - for _, dep := range deps { - g.AddEdge(dep, r) - } - } - - return algorithms.TopsortGraph(g) -} - -func findDependencies(repo string) ([]string, error) { - man, err := manifest.Read(manifestPath(repo)) - if err != nil { - return nil, err - } - - return lo.FilterMap(man.Dependencies, func(d *manifest.Dependency, ind int) (string, bool) { return d.Repo, isRepo(d.Repo) }), nil -} - -func AllDependencies(repos []string) ([]string, error) { - deps := []string{} - visit := func(r string) error { - deps = append(deps, r) - return nil - } - for _, repo := range repos { - if err := algorithms.DFS(repo, findDependencies, visit); err != nil { - return deps, err - } - } - - return TopSortNames(lo.Uniq(deps)) -} - -func Dependencies(repo string) ([]string, error) { - // dfs from the repo to find all dependencies - deps := []string{} - visit := func(r string) error { - deps = append(deps, r) - return nil - } - if err := algorithms.DFS(repo, findDependencies, visit); err != nil { - return nil, err - } - - // topsort only those to find correct ordering - return TopSortNames(deps) -} - -func UntilRepo(client api.Client, repo string, installations []*api.Installation) ([]*api.Installation, error) { - topsorted, err := TopSort(client, installations) - if err != nil || repo == "" { - return topsorted, err - } - - ind := algorithms.Index(topsorted, func(i *api.Installation) bool { return i.Repository.Name == repo }) - return topsorted[:(ind + 1)], err -} diff --git a/pkg/wkspace/manifest.go b/pkg/wkspace/manifest.go deleted file mode 100644 index 0bcd2668e..000000000 --- a/pkg/wkspace/manifest.go +++ /dev/null @@ -1,82 +0,0 @@ -package wkspace - -import ( - "github.com/pluralsh/plural-cli/pkg/api" - "github.com/pluralsh/plural-cli/pkg/manifest" -) - -func (wk *Workspace) BuildManifest(prev *manifest.Manifest) *manifest.Manifest { - repository := wk.Installation.Repository - charts := make([]*manifest.ChartManifest, len(wk.Charts)) - terraform := make([]*manifest.TerraformManifest, len(wk.Terraform)) - - for i, ci := range wk.Charts { - charts[i] = buildChartManifest(ci) - } - for i, ti := range wk.Terraform { - terraform[i] = buildTerraformManifest(ti) - } - - return &manifest.Manifest{ - Id: repository.Id, - Name: repository.Name, - Cluster: wk.Provider.Cluster(), - Project: wk.Provider.Project(), - Bucket: wk.Provider.Bucket(), - Provider: wk.Provider.Name(), - Region: wk.Provider.Region(), - License: wk.Installation.LicenseKey, - Wait: wk.requiresWait(), - Charts: charts, - Terraform: terraform, - Dependencies: buildDependencies(repository.Name, wk.Charts, wk.Terraform), - Context: wk.Provider.Context(), - Links: prev.Links, - } -} - -func buildDependencies(repo string, charts []*api.ChartInstallation, tfs []*api.TerraformInstallation) []*manifest.Dependency { - var deps []*manifest.Dependency - var seen = make(map[string]bool) - - for _, chart := range charts { - for _, dep := range chart.Chart.Dependencies.Dependencies { - _, ok := seen[dep.Repo] - if ok { - continue - } - - if dep.Repo != repo { - deps = append(deps, &manifest.Dependency{Repo: dep.Repo}) - seen[dep.Repo] = true - } - } - } - - for _, tf := range tfs { - for _, dep := range tf.Terraform.Dependencies.Dependencies { - _, ok := seen[dep.Repo] - if ok { - continue - } - - if dep.Repo != repo { - deps = append(deps, &manifest.Dependency{Repo: dep.Repo}) - seen[dep.Repo] = true - } - } - } - - return deps -} - -func buildChartManifest(chartInstallation *api.ChartInstallation) *manifest.ChartManifest { - chart := chartInstallation.Chart - version := chartInstallation.Version - return &manifest.ChartManifest{Id: chart.Id, Name: chart.Name, VersionId: version.Id, Version: version.Version} -} - -func buildTerraformManifest(tfInstallation *api.TerraformInstallation) *manifest.TerraformManifest { - terraform := tfInstallation.Terraform - return &manifest.TerraformManifest{Id: terraform.Id, Name: terraform.Name} -} diff --git a/pkg/wkspace/notes.go b/pkg/wkspace/notes.go deleted file mode 100644 index 25c18bd27..000000000 --- a/pkg/wkspace/notes.go +++ /dev/null @@ -1 +0,0 @@ -package wkspace diff --git a/pkg/wkspace/validator.go b/pkg/wkspace/validator.go index 92cd81487..b75b186da 100644 --- a/pkg/wkspace/validator.go +++ b/pkg/wkspace/validator.go @@ -34,22 +34,6 @@ func Preflight() (bool, error) { return true, nil } -func (wk *Workspace) Validate() error { - for _, tf := range wk.Terraform { - if err := wk.providersValid(tf.Terraform.Dependencies.Providers); err != nil { - return err - } - } - - for _, chart := range wk.Charts { - if err := wk.providersValid(chart.Chart.Dependencies.Providers); err != nil { - return err - } - } - - return nil -} - func (wk *Workspace) providersValid(providers []string) error { if len(providers) == 0 { return nil