Skip to content

Commit

Permalink
feat: clone tenant (#184)
Browse files Browse the repository at this point in the history
  • Loading branch information
benPearce1 authored Dec 21, 2022
1 parent f4427a7 commit 9bc3b6d
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 3 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.19
require (
github.com/AlecAivazis/survey/v2 v2.3.6
github.com/MakeNowJust/heredoc/v2 v2.0.1
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.18.0
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.18.1
github.com/briandowns/spinner v1.19.0
github.com/google/uuid v1.3.0
github.com/hashicorp/go-multierror v1.1.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ github.com/MakeNowJust/heredoc/v2 v2.0.1 h1:rlCHh70XXXv7toz95ajQWOWQnN4WNLt0TdpZ
github.com/MakeNowJust/heredoc/v2 v2.0.1/go.mod h1:6/2Abh5s+hc3g9nbWLe9ObDIOhaRrqsyY9MWy+4JdRM=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.18.0 h1:EfQGnIT6/89CeSTibq3AWaHN0YXPggPwoW9lW18dhrE=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.18.0/go.mod h1:yCrdCpNf9D60Q6zYeG7wt9XHimM0r9z0AY9auR6AjLc=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.18.1 h1:Bhae8Z8vzvZus2coXQs0EKN+22tonak9nVMI1ArzvEc=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.18.1/go.mod h1:yCrdCpNf9D60Q6zYeG7wt9XHimM0r9z0AY9auR6AjLc=
github.com/briandowns/spinner v1.19.0 h1:s8aq38H+Qju89yhp89b4iIiMzMm8YN3p6vGpwyh/a8E=
github.com/briandowns/spinner v1.19.0/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/ppAL/hX5SmPJU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
Expand Down
135 changes: 135 additions & 0 deletions pkg/cmd/tenant/clone/clone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package clone

import (
"fmt"
"github.com/MakeNowJust/heredoc/v2"
"github.com/OctopusDeploy/cli/pkg/cmd"
"github.com/OctopusDeploy/cli/pkg/cmd/tenant/shared"
"github.com/OctopusDeploy/cli/pkg/constants"
"github.com/OctopusDeploy/cli/pkg/factory"
"github.com/OctopusDeploy/cli/pkg/machinescommon"
"github.com/OctopusDeploy/cli/pkg/output"
"github.com/OctopusDeploy/cli/pkg/question"
"github.com/OctopusDeploy/cli/pkg/question/selectors"
"github.com/OctopusDeploy/cli/pkg/util/flag"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants"
"github.com/spf13/cobra"
)

const (
FlagName = "name"
FlagDescription = "description"
FlagSourceTenant = "source-tenant"
)

type CloneFlags struct {
Name *flag.Flag[string]
Description *flag.Flag[string]
SourceTenant *flag.Flag[string]
*machinescommon.WebFlags
}

func NewCloneFlags() *CloneFlags {
return &CloneFlags{
Name: flag.New[string](FlagName, false),
Description: flag.New[string](FlagDescription, false),
SourceTenant: flag.New[string](FlagSourceTenant, false),
}
}

type CloneOptions struct {
*CloneFlags
*cmd.Dependencies
GetTenantCallback shared.GetTenantCallback
GetAllTenantsCallback shared.GetAllTenantsCallback
}

func NewCloneOptions(flags *CloneFlags, dependencies *cmd.Dependencies) *CloneOptions {
return &CloneOptions{
CloneFlags: flags,
Dependencies: dependencies,
GetTenantCallback: func(id string) (*tenants.Tenant, error) {
return shared.GetTenant(*dependencies.Client, id)
},
GetAllTenantsCallback: func() ([]*tenants.Tenant, error) {
return shared.GetAllTenants(*dependencies.Client)
},
}
}

func NewCmdClone(f factory.Factory) *cobra.Command {
cloneFlags := NewCloneFlags()
cmd := &cobra.Command{
Use: "clone",
Short: "Clone a tenant",
Long: "Clone a tenant in Octopus Deploy",
Example: heredoc.Docf(`
$ %[1]s tenant clone
$ %[1]s tenant clone --name "Garys Cakes" --source-tenant "Bobs Wood Shop"
`, constants.ExecutableName),
RunE: func(c *cobra.Command, args []string) error {
opts := NewCloneOptions(cloneFlags, cmd.NewDependencies(f, c))

return cloneRun(opts)
},
}

flags := cmd.Flags()
flags.StringVarP(&cloneFlags.Name.Value, cloneFlags.Name.Name, "n", "", "Name of the new tenant")
flags.StringVarP(&cloneFlags.Description.Value, cloneFlags.Description.Name, "d", "", "Description of the new tenant")
flags.StringVar(&cloneFlags.SourceTenant.Value, cloneFlags.SourceTenant.Name, "", "Name of the source tenant")
return cmd
}

func cloneRun(opts *CloneOptions) error {
if !opts.NoPrompt {
if err := PromptMissing(opts); err != nil {
return err
}
}

tenant, err := opts.GetTenantCallback(opts.SourceTenant.Value)
if err != nil {
return err
}

clonedTenant, err := opts.Client.Tenants.Clone(tenant, tenants.TenantCloneRequest{Name: opts.Name.Value, Description: opts.Description.Value})
if err != nil {
return err
}

fmt.Fprintf(opts.Out, "Successfully cloned tenant '%s' to '%s'.\n", tenant.Name, clonedTenant.Name)
if !opts.NoPrompt {
autoCmd := flag.GenerateAutomationCmd(opts.CmdPath, opts.SourceTenant, opts.Name, opts.Description)
fmt.Fprintf(opts.Out, "\nAutomation Command: %s\n", autoCmd)
}

link := output.Bluef("%s/app#/%s/tenants/%s/overview", opts.Host, opts.Space.GetID(), clonedTenant.GetID())
fmt.Fprintf(opts.Out, "View this tenant on Octopus Deploy: %s\n", link)

return nil
}

func PromptMissing(opts *CloneOptions) error {
err := question.AskName(opts.Ask, "", "Tenant", &opts.Name.Value)
if err != nil {
return err
}
err = question.AskDescription(opts.Ask, "", "Tenant", &opts.Description.Value)
if err != nil {
return err
}

if opts.SourceTenant.Value == "" {
tenant, err := selectors.Select(opts.Ask, "You have not specified a source Tenant to clone from. Please select one:", opts.GetAllTenantsCallback, func(tenant *tenants.Tenant) string {
return tenant.Name
})
if err != nil {
return nil
}

opts.SourceTenant.Value = tenant.Name
}

return nil
}
58 changes: 58 additions & 0 deletions pkg/cmd/tenant/clone/clone_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package clone_test

import (
"github.com/OctopusDeploy/cli/pkg/cmd"
"github.com/OctopusDeploy/cli/pkg/cmd/tenant/clone"
"github.com/OctopusDeploy/cli/test/testutil"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants"
"github.com/stretchr/testify/assert"
"testing"
)

func TestPromptMissing_AllFlagsSupplied(t *testing.T) {
pa := []*testutil.PA{}
asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
flags := clone.NewCloneFlags()
flags.Name.Value = "Cloned tenant"
flags.Description.Value = "the description"
flags.SourceTenant.Value = "source tenant"

opts := clone.NewCloneOptions(flags, &cmd.Dependencies{Ask: asker})

opts.GetTenantCallback = func(identifier string) (*tenants.Tenant, error) {
return tenants.NewTenant("source tenant"), nil
}

err := clone.PromptMissing(opts)
checkRemainingPrompts()
assert.NoError(t, err)
}

func TestPromptMissing_NoFlagsSupplied(t *testing.T) {
pa := []*testutil.PA{
testutil.NewInputPrompt("Name", "A short, memorable, unique name for this Tenant.", "cloned tenant"),
testutil.NewInputPrompt("Description", "A short, memorable, description for this Tenant.", "the description"),
testutil.NewSelectPrompt("You have not specified a source Tenant to clone from. Please select one:", "", []string{"source tenant", "source tenant 2"}, "source tenant"),
}
asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
flags := clone.NewCloneFlags()
opts := clone.NewCloneOptions(flags, &cmd.Dependencies{Ask: asker})

opts.GetTenantCallback = func(identifier string) (*tenants.Tenant, error) {
return tenants.NewTenant("source tenant"), nil
}
opts.GetAllTenantsCallback= func () ([]*tenants.Tenant, error) {
return []*tenants.Tenant{
tenants.NewTenant("source tenant"),
tenants.NewTenant("source tenant 2"),
},nil
}

err := clone.PromptMissing(opts)
checkRemainingPrompts()
assert.NoError(t, err)

assert.Equal(t, "cloned tenant", flags.Name.Value)
assert.Equal(t, "the description", flags.Description.Value)
assert.Equal(t, "source tenant", flags.SourceTenant.Value)
}
2 changes: 2 additions & 0 deletions pkg/cmd/tenant/tenant.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
cmdDisconnect "github.com/OctopusDeploy/cli/pkg/cmd/tenant/disconnect"
cmdList "github.com/OctopusDeploy/cli/pkg/cmd/tenant/list"
cmdTag "github.com/OctopusDeploy/cli/pkg/cmd/tenant/tag"
cmdClone "github.com/OctopusDeploy/cli/pkg/cmd/tenant/clone"
cmdView "github.com/OctopusDeploy/cli/pkg/cmd/tenant/view"
"github.com/OctopusDeploy/cli/pkg/constants"
"github.com/OctopusDeploy/cli/pkg/constants/annotations"
Expand All @@ -34,6 +35,7 @@ func NewCmdTenant(f factory.Factory) *cobra.Command {
cmd.AddCommand(cmdList.NewCmdList(f))
cmd.AddCommand(cmdCreate.NewCmdCreate(f))
cmd.AddCommand(cmdTag.NewCmdTag(f))
cmd.AddCommand(cmdClone.NewCmdClone(f))
cmd.AddCommand(cmdDelete.NewCmdDelete(f))
cmd.AddCommand(cmdView.NewCmdView(f))

Expand Down

0 comments on commit 9bc3b6d

Please sign in to comment.