From ab99a780ddd8f7cb3444c553ba07ab4d7947cf52 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 4 Dec 2024 15:40:34 +0100 Subject: [PATCH] replace ops and workspace commands with mgmt --- cmd/command/ops/ops.go | 80 --------- cmd/command/ops/ops_test.go | 140 --------------- cmd/command/plural/plural.go | 6 +- cmd/command/workspace/workspace.go | 227 ------------------------ cmd/command/workspace/workspace_test.go | 141 --------------- pkg/provider/provider.go | 7 +- 6 files changed, 4 insertions(+), 597 deletions(-) delete mode 100644 cmd/command/ops/ops.go delete mode 100644 cmd/command/ops/ops_test.go delete mode 100644 cmd/command/workspace/workspace.go delete mode 100644 cmd/command/workspace/workspace_test.go diff --git a/cmd/command/ops/ops.go b/cmd/command/ops/ops.go deleted file mode 100644 index c6fdc6860..000000000 --- a/cmd/command/ops/ops.go +++ /dev/null @@ -1,80 +0,0 @@ -package ops - -import ( - "fmt" - - "github.com/pluralsh/plural-cli/pkg/client" - "github.com/pluralsh/plural-cli/pkg/common" - "github.com/pluralsh/plural-cli/pkg/provider" - "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: "ops", - Usage: "Commands for simplifying cluster operations", - Subcommands: p.opsCommands(), - Category: "Debugging", - } -} - -func (p *Plural) opsCommands() []cli.Command { - return []cli.Command{ - { - Name: "terminate", - Usage: "terminates a worker node in your cluster", - ArgsUsage: "{name}", - Action: common.LatestVersion(common.RequireArgs(common.InitKubeconfig(p.handleTerminateNode), []string{"{name}"})), - }, - { - Name: "cluster", - Usage: "list the nodes in your cluster", - Action: common.LatestVersion(common.InitKubeconfig(p.handleListNodes)), - }, - } -} - -func (p *Plural) handleListNodes(c *cli.Context) error { - if err := p.InitKube(); err != nil { - return err - } - nodes, err := p.Nodes() - if err != nil { - return err - } - return common.PrintListNodes(nodes) -} - -func (p *Plural) handleTerminateNode(c *cli.Context) error { - name := c.Args().Get(0) - provider, err := getProvider() - if err != nil { - return err - } - if err := p.InitKube(); err != nil { - return err - } - node, err := p.Node(name) - if err != nil { - return err - } - - return provider.Decommision(node) -} - -func getProvider() (provider.Provider, error) { - _, found := utils.ProjectRoot() - if !found { - return nil, fmt.Errorf("project not initialized, run `plural init` to set up a workspace") - } - - return provider.GetProvider() -} diff --git a/cmd/command/ops/ops_test.go b/cmd/command/ops/ops_test.go deleted file mode 100644 index 22e8642d3..000000000 --- a/cmd/command/ops/ops_test.go +++ /dev/null @@ -1,140 +0,0 @@ -package ops_test - -import ( - "os" - "testing" - - "github.com/pluralsh/plural-cli/pkg/common" - - "github.com/pluralsh/plural-cli/cmd/command/plural" - clientcmd "github.com/pluralsh/plural-cli/pkg/client" - "github.com/pluralsh/plural-cli/pkg/manifest" - "github.com/pluralsh/plural-cli/pkg/test/mocks" - "github.com/pluralsh/plural-cli/pkg/utils/git" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "gopkg.in/yaml.v2" - - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestListNodes(t *testing.T) { - tests := []struct { - name string - args []string - nodes *v1.NodeList - expectedResponse string - }{ - { - name: `test "ops cluster"`, - args: []string{plural.ApplicationName, "ops", "cluster"}, - nodes: &v1.NodeList{ - Items: []v1.Node{ - { - ObjectMeta: metav1.ObjectMeta{Name: "cluster-1"}, - Spec: v1.NodeSpec{}, - Status: v1.NodeStatus{}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "cluster-2"}, - Spec: v1.NodeSpec{}, - Status: v1.NodeStatus{}, - }, - }, - }, - expectedResponse: `+-----------+-----+--------+--------+------+ -| NAME | CPU | MEMORY | REGION | ZONE | -+-----------+-----+--------+--------+------+ -| cluster-1 | 0 | 0 | | | -| cluster-2 | 0 | 0 | | | -+-----------+-----+--------+--------+------+ -`, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - client := mocks.NewClient(t) - kube := mocks.NewKube(t) - kube.On("Nodes").Return(test.nodes, nil) - app := plural.CreateNewApp(&plural.Plural{ - Plural: clientcmd.Plural{ - Client: client, - Kube: kube, - }, - }) - app.HelpName = plural.ApplicationName - os.Args = test.args - res, err := common.CaptureStdout(app, os.Args) - assert.NoError(t, err) - - assert.Equal(t, test.expectedResponse, res) - }) - } -} - -func TestTerminate(t *testing.T) { - tests := []struct { - name string - args []string - node *v1.Node - pm manifest.ProjectManifest - expectedResponse string - }{ - { - name: `test "ops terminate"`, - args: []string{plural.ApplicationName, "ops", "terminate", "cluster-1"}, - node: &v1.Node{ - - ObjectMeta: metav1.ObjectMeta{Name: "cluster-1"}, - Spec: v1.NodeSpec{}, - Status: v1.NodeStatus{}, - }, - pm: manifest.ProjectManifest{ - Cluster: "test", - Bucket: "test", - Project: "test", - Provider: "test", - Region: "test", - }, - expectedResponse: ``, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - // create temp environment - currentDir, err := os.Getwd() - assert.NoError(t, err) - dir, err := os.MkdirTemp("", "config") - assert.NoError(t, err) - defer func(path, currentDir string) { - _ = os.RemoveAll(path) - _ = os.Chdir(currentDir) - }(dir, currentDir) - err = os.Chdir(dir) - assert.NoError(t, err) - _, err = git.Init() - assert.NoError(t, err) - data, err := yaml.Marshal(test.pm) - assert.NoError(t, err) - err = os.WriteFile("workspace.yaml", data, os.FileMode(0755)) - assert.NoError(t, err) - - client := mocks.NewClient(t) - kube := mocks.NewKube(t) - kube.On("Node", mock.AnythingOfType("string")).Return(test.node, nil) - app := plural.CreateNewApp(&plural.Plural{ - Plural: clientcmd.Plural{ - Client: client, - Kube: kube, - }, - }) - app.HelpName = plural.ApplicationName - os.Args = test.args - res, err := common.CaptureStdout(app, os.Args) - assert.NoError(t, err) - - assert.Equal(t, test.expectedResponse, res) - }) - } -} diff --git a/cmd/command/plural/plural.go b/cmd/command/plural/plural.go index 474011f7e..9ba36a50e 100644 --- a/cmd/command/plural/plural.go +++ b/cmd/command/plural/plural.go @@ -10,13 +10,12 @@ import ( cryptocmd "github.com/pluralsh/plural-cli/cmd/command/crypto" "github.com/pluralsh/plural-cli/cmd/command/down" cmdinit "github.com/pluralsh/plural-cli/cmd/command/init" - "github.com/pluralsh/plural-cli/cmd/command/ops" + "github.com/pluralsh/plural-cli/cmd/command/mgmt" "github.com/pluralsh/plural-cli/cmd/command/pr" "github.com/pluralsh/plural-cli/cmd/command/profile" "github.com/pluralsh/plural-cli/cmd/command/up" "github.com/pluralsh/plural-cli/cmd/command/version" "github.com/pluralsh/plural-cli/cmd/command/vpn" - "github.com/pluralsh/plural-cli/cmd/command/workspace" "github.com/pluralsh/plural-cli/pkg/client" "github.com/pluralsh/plural-cli/pkg/common" conf "github.com/pluralsh/plural-cli/pkg/config" @@ -162,12 +161,11 @@ func CreateNewApp(plural *Plural) *cli.App { cryptocmd.Command(plural.Plural), clone.Command(), down.Command(), - ops.Command(plural.Plural), + mgmt.Command(plural.Plural), profile.Command(), pr.Command(plural.Plural), cmdinit.Command(plural.Plural), up.Command(plural.Plural), - workspace.Command(plural.Plural, plural.HelmConfiguration), vpn.Command(plural.Plural), version.Command(), } diff --git a/cmd/command/workspace/workspace.go b/cmd/command/workspace/workspace.go deleted file mode 100644 index 0d7214f2f..000000000 --- a/cmd/command/workspace/workspace.go +++ /dev/null @@ -1,227 +0,0 @@ -package workspace - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/pluralsh/plural-cli/pkg/client" - "github.com/pluralsh/plural-cli/pkg/common" - "github.com/pluralsh/plural-cli/pkg/helm" - "github.com/pluralsh/plural-cli/pkg/provider" - "github.com/pluralsh/plural-cli/pkg/utils" - "github.com/pluralsh/plural-cli/pkg/wkspace" - "github.com/urfave/cli" - "helm.sh/helm/v3/pkg/action" -) - -type Plural struct { - client.Plural - HelmConfiguration *action.Configuration -} - -func Command(clients client.Plural, helmConfiguration *action.Configuration) cli.Command { - p := Plural{ - Plural: clients, - HelmConfiguration: helmConfiguration, - } - return cli.Command{ - Name: "workspace", - Aliases: []string{"wkspace"}, - Usage: "Commands for managing installations in your workspace", - Subcommands: p.workspaceCommands(), - Category: "Workspace", - } -} - -func (p *Plural) workspaceCommands() []cli.Command { - return []cli.Command{ - { - Name: "kube-init", - Usage: "generates kubernetes credentials for this subworkspace", - Action: common.LatestVersion(kubeInit), - }, - { - Name: "helm", - Usage: "upgrade/installs the helm chart for this subworkspace", - ArgsUsage: "{name}", - Flags: []cli.Flag{ - cli.StringSliceFlag{ - Name: "skip", - Usage: "helm sub-chart to skip. can be passed multiple times", - }, - cli.StringSliceFlag{ - Name: "set", - Usage: "helm value to set. can be passed multiple times", - }, - cli.StringSliceFlag{ - Name: "setJSON", - Usage: "JSON helm value to set. can be passed multiple times", - }, - cli.BoolFlag{ - Name: "wait", - Usage: "have helm wait until all pods are in ready state", - }, - }, - Action: common.LatestVersion(common.InitKubeconfig(p.bounceHelm)), - }, - { - Name: "helm-diff", - Usage: "diffs the helm release for this subworkspace", - ArgsUsage: "{name}", - Action: common.LatestVersion(p.diffHelm), - }, - { - Name: "helm-deps", - Usage: "updates the helm dependencies for this workspace", - ArgsUsage: "{path}", - Action: common.LatestVersion(updateDeps), - }, - { - Name: "terraform-diff", - Usage: "diffs the helm release for this subworkspace", - ArgsUsage: "{name}", - Action: common.LatestVersion(p.diffTerraform), - }, - { - Name: "crds", - Usage: "installs the crds for this repo", - ArgsUsage: "{name}", - Action: common.LatestVersion(common.InitKubeconfig(p.createCrds)), - }, - { - Name: "helm-template", - Usage: "templates the helm values to stdout", - ArgsUsage: "{name}", - Action: common.LatestVersion(common.RequireArgs(p.templateHelm, []string{"{name}"})), - }, - { - Name: "helm-mapkubeapis", - Usage: "updates in-place Helm release metadata that contains deprecated or removed Kubernetes APIs to a new instance with supported Kubernetes APIs", - ArgsUsage: "{name}", - Action: common.LatestVersion(common.RequireArgs(p.mapkubeapis, []string{"{name}"})), - }, - } -} - -func kubeInit(_ *cli.Context) error { - _, found := utils.ProjectRoot() - if !found { - return fmt.Errorf("Project not initialized, run `plural init` to set up a workspace") - } - - prov, err := provider.GetProvider() - if err != nil { - return err - } - - return prov.KubeConfig() -} - -func (p *Plural) bounceHelm(c *cli.Context) error { - name := c.Args().Get(0) - minimal, err := wkspace.Minimal(name, p.HelmConfiguration) - if err != nil { - return err - } - - var skipArgs []string - if c.IsSet("skip") { - for _, skipChart := range c.StringSlice("skip") { - skipString := fmt.Sprintf("%s.enabled=false", skipChart) - skipArgs = append(skipArgs, skipString) - } - } - var setArgs []string - if c.IsSet("set") { - setArgs = append(setArgs, c.StringSlice("set")...) - } - - var setJSONArgs []string - if c.IsSet("setJSON") { - setJSONArgs = append(setJSONArgs, c.StringSlice("setJSON")...) - } - - return minimal.BounceHelm(c.IsSet("wait"), skipArgs, setArgs, setJSONArgs) -} - -func (p *Plural) diffHelm(c *cli.Context) error { - name := c.Args().Get(0) - minimal, err := wkspace.Minimal(name, p.HelmConfiguration) - if err != nil { - return err - } - - return minimal.DiffHelm() -} - -func (p *Plural) diffTerraform(c *cli.Context) error { - name := c.Args().Get(0) - minimal, err := wkspace.Minimal(name, p.HelmConfiguration) - if err != nil { - return err - } - - return minimal.DiffTerraform() -} - -func (p *Plural) createCrds(_ *cli.Context) error { - err := p.InitKube() - if err != nil { - return err - } - if empty, err := utils.IsEmpty("crds"); err != nil || empty { - return nil - } - - return filepath.Walk("crds", func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if info.IsDir() { - return nil - } - - err = p.Kube.Apply(path, true) - if err != nil { - errStr := fmt.Sprint(err) - if strings.Contains(errStr, "invalid apiVersion \"client.authentication.k8s.io/v1alpha1\"") { - return fmt.Errorf("failed with %s, this is usually due to your aws cli version being out of date", errStr) - } - return err - } - - return nil - }) -} - -func updateDeps(c *cli.Context) error { - path := c.Args().Get(0) - if path == "" { - path = "." - } - - return helm.UpdateDependencies(path) -} - -func (p *Plural) templateHelm(c *cli.Context) error { - name := c.Args().Get(0) - minimal, err := wkspace.Minimal(name, p.HelmConfiguration) - if err != nil { - return err - } - - return minimal.TemplateHelm() -} - -func (p *Plural) mapkubeapis(c *cli.Context) error { - name := c.Args().Get(0) - minimal, err := wkspace.Minimal(name, p.HelmConfiguration) - if err != nil { - return err - } - - return minimal.MapKubeApis() -} diff --git a/cmd/command/workspace/workspace_test.go b/cmd/command/workspace/workspace_test.go deleted file mode 100644 index 316deb844..000000000 --- a/cmd/command/workspace/workspace_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package workspace_test - -import ( - "io" - "os" - "path/filepath" - "testing" - - "github.com/pluralsh/plural-cli/pkg/common" - - "github.com/pluralsh/plural-cli/cmd/command/plural" - "github.com/pluralsh/plural-cli/pkg/manifest" - "github.com/pluralsh/plural-cli/pkg/utils" - "github.com/pluralsh/plural-cli/pkg/utils/git" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v2" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" - kubefake "helm.sh/helm/v3/pkg/kube/fake" - "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/storage" - "helm.sh/helm/v3/pkg/storage/driver" -) - -const subchart = "subchart" - -func TestHelmCommands(t *testing.T) { - // create temp environment - currentDir, err := os.Getwd() - assert.NoError(t, err) - dir, err := os.MkdirTemp("", "config") - assert.NoError(t, err) - defer func(path, currentDir string) { - _ = os.RemoveAll(path) - _ = os.Chdir(currentDir) - }(dir, currentDir) - tFiles, err := filepath.Abs("../../../pkg/test/helm") - assert.NoError(t, err) - err = utils.CopyDir(tFiles, filepath.Join(dir, subchart)) - assert.NoError(t, err) - err = os.Chdir(dir) - assert.NoError(t, err) - _, err = git.Init() - assert.NoError(t, err) - data, err := yaml.Marshal(manifest.ProjectManifest{ - Cluster: "test", - Bucket: "test", - Project: "test", - Provider: "test", - Region: "test", - }) - assert.NoError(t, err) - err = os.WriteFile("workspace.yaml", data, os.FileMode(0755)) - assert.NoError(t, err) - - tests := []struct { - name string - args []string - expectedOutput string - store *storage.Storage - directory string - }{ - { - name: `test helm-template`, - args: []string{plural.ApplicationName, "workspace", "helm-template", subchart}, - expectedOutput: "subchart/helm/output/template.txt", - store: storageFixture(), - directory: dir, - }, - { - name: `test helm install`, - args: []string{plural.ApplicationName, "workspace", "helm", subchart}, - store: storageFixture(), - directory: filepath.Join(dir, subchart), - }, - { - name: `test helm upgrade`, - args: []string{plural.ApplicationName, "workspace", "helm", subchart}, - store: storageReleaseDeployed(t), - directory: filepath.Join(dir, subchart), - }, - { - name: `test helm-diff`, - args: []string{plural.ApplicationName, "workspace", "helm-diff", subchart}, - expectedOutput: "subchart/helm/output/diff.txt", - store: storageReleaseDeployed(t), - directory: dir, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - actionConfig := &action.Configuration{ - Releases: test.store, - KubeClient: &kubefake.PrintingKubeClient{Out: io.Discard}, - Capabilities: chartutil.DefaultCapabilities, - Log: func(format string, v ...interface{}) {}, - } - err = os.Chdir(test.directory) - assert.NoError(t, err) - defer func() { - err := os.Chdir(dir) - assert.NoError(t, err) - }() - - app := plural.CreateNewApp(&plural.Plural{HelmConfiguration: actionConfig}) - app.HelpName = plural.ApplicationName - os.Args = test.args - output, err := common.CaptureStdout(app, os.Args) - assert.NoError(t, err) - if test.expectedOutput != "" { - expected, err := utils.ReadFile(test.expectedOutput) - assert.NoError(t, err) - assert.Equal(t, expected, output) - } - }) - } -} - -func storageFixture() *storage.Storage { - return storage.Init(driver.NewMemory()) -} - -func storageReleaseDeployed(t *testing.T) *storage.Storage { - fixture := storageFixture() - err := fixture.Create(&release.Release{ - Name: "subchart", - Info: &release.Info{Status: release.StatusDeployed}, - Chart: &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "Myrelease-Chart", - Version: "1.2.3", - }, - }, - Version: 1, - }) - if err != nil { - t.Fatal("can't create storage") - } - return fixture -} diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 96d4905ba..f87e80dfb 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -5,15 +5,13 @@ import ( "strings" "github.com/AlecAivazis/survey/v2" - "github.com/pluralsh/polly/algorithms" - "github.com/pluralsh/polly/containers" - v1 "k8s.io/api/core/v1" - "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/permissions" "github.com/pluralsh/plural-cli/pkg/utils" + "github.com/pluralsh/polly/algorithms" + "github.com/pluralsh/polly/containers" ) var cloudFlag bool @@ -30,7 +28,6 @@ type Provider interface { CreateBackend(prefix string, version string, ctx map[string]interface{}) (string, error) CreateBucket() error Context() map[string]interface{} - Decommision(node *v1.Node) error Preflights() []*Preflight Permissions() (permissions.Checker, error) Flush() error