Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions pkg/cmd/target/list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/OctopusDeploy/cli/pkg/output"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
"github.com/spf13/cobra"
"strconv"
)

type ListOptions struct {
Expand Down Expand Up @@ -75,12 +76,12 @@ func ListRun(opts *ListOptions) error {
return shared.GetDeploymentTargetAsJson(opts.Dependencies, item)
},
Table: output.TableDefinition[*machines.DeploymentTarget]{
Header: []string{"NAME", "TYPE", "ROLES", "ENVIRONMENTS", "TENANTS", "TAGS", "DEFAULT WORKER POOL"},
Header: []string{"NAME", "TYPE", "IS DISABLED", "ROLES", "ENVIRONMENTS", "TENANTS", "TAGS", "DEFAULT WORKER POOL"},
Row: func(item *machines.DeploymentTarget) []string {
environmentNames := resolveValues(item.EnvironmentIDs, environmentMap)
tenantNames := resolveValues(item.TenantIDs, tenantMap)
workerPool := shared.ResolveDefaultWorkerPool(item, workerPoolMap, "None")
return []string{output.Bold(item.Name), describeTargetType(item), output.FormatAsList(item.Roles), output.FormatAsList(environmentNames), output.FormatAsList(tenantNames), output.FormatAsList(item.TenantTags), workerPool}
return []string{output.Bold(item.Name), describeTargetType(item), strconv.FormatBool(item.IsDisabled), output.FormatAsList(item.Roles), output.FormatAsList(environmentNames), output.FormatAsList(tenantNames), output.FormatAsList(item.TenantTags), workerPool}
},
},
Basic: func(item *machines.DeploymentTarget) string {
Expand Down
106 changes: 106 additions & 0 deletions pkg/cmd/target/list/list_disabled_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package list_test

import (
"bytes"
"testing"

cmdRoot "github.com/OctopusDeploy/cli/pkg/cmd/root"
"github.com/OctopusDeploy/cli/pkg/question"
"github.com/OctopusDeploy/cli/test/fixtures"
"github.com/OctopusDeploy/cli/test/testutil"
octopusApiClient "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
octopusConstants "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/constants"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/environments"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/resources"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/workerpools"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
)

// the shared root resource has no worker pool link; the target commands need it
var rootResource = newRootResourceWithWorkerPools()

func newRootResourceWithWorkerPools() *octopusApiClient.RootResource {
root := testutil.NewRootResource()
root.Links[octopusConstants.LinkWorkerPools] = octopusConstants.TestURIWorkerPools
return root
}

const spaceID = "Spaces-1"

func TestDeploymentTargetListShowsDisabledState(t *testing.T) {
space1 := fixtures.NewSpace(spaceID, "Default Space")

tests := []struct {
name string
run func(t *testing.T, api *testutil.MockHttpServer, qa *testutil.AskMocker, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer)
}{
{"table output has an IS DISABLED column", func(t *testing.T, api *testutil.MockHttpServer, qa *testutil.AskMocker, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"deployment-target", "list", "--no-prompt", "-f", "table"})
return rootCmd.ExecuteC()
})

respondWithTargets(t, api)

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)
assert.Contains(t, stdOut.String(), "IS DISABLED")
assert.Regexp(t, `web-server.*false`, stdOut.String())
assert.Regexp(t, `db-server.*true`, stdOut.String())
assert.Equal(t, "", stdErr.String())
}},

{"json output carries IsDisabled", func(t *testing.T, api *testutil.MockHttpServer, qa *testutil.AskMocker, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"deployment-target", "list", "--no-prompt", "-f", "json"})
return rootCmd.ExecuteC()
})

respondWithTargets(t, api)
// the json mapper re-resolves the lookups per target
for range 2 {
api.ExpectRequest(t, "GET", "/api/Spaces-1/environments/all").RespondWith([]*environments.Environment{development})
api.ExpectRequest(t, "GET", "/api/Spaces-1/tenants/all").RespondWith([]*tenants.Tenant{})
}

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)
assert.Contains(t, stdOut.String(), `"IsDisabled": false`)
assert.Contains(t, stdOut.String(), `"IsDisabled": true`)
assert.Equal(t, "", stdErr.String())
}},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
api, qa := testutil.NewMockServerAndAsker()
askProvider := question.NewAskProvider(qa.AsAsker())
fac := testutil.NewMockFactoryWithSpaceAndPrompt(api, space1, askProvider)
rootCmd := cmdRoot.NewCmdRoot(fac, nil, askProvider)
rootCmd.SetOut(stdout)
rootCmd.SetErr(stderr)
test.run(t, api, qa, rootCmd, stdout, stderr)
})
}
}

func respondWithTargets(t *testing.T, api *testutil.MockHttpServer) {
api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1/machines?take=2147483647").
RespondWith(resources.Resources[*machines.DeploymentTarget]{Items: []*machines.DeploymentTarget{
fixtures.NewDeploymentTarget(spaceID, "Machines-100", "web-server", false),
fixtures.NewDeploymentTarget(spaceID, "Machines-200", "db-server", true),
}})
api.ExpectRequest(t, "GET", "/api/Spaces-1/environments/all").RespondWith([]*environments.Environment{development})
api.ExpectRequest(t, "GET", "/api/Spaces-1/tenants/all").RespondWith([]*tenants.Tenant{})
api.ExpectRequest(t, "GET", "/api/Spaces-1/workerpools/all").RespondWith([]*workerpools.WorkerPoolListResult{})
}

var development = fixtures.NewEnvironment(spaceID, "Environments-1", "Development")
2 changes: 2 additions & 0 deletions pkg/cmd/target/shared/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type DeploymentTargetAsJson struct {
Name string `json:"Name"`
HealthStatus string `json:"HealthStatus"`
StatusSummary string `json:"StatusSummary"`
IsDisabled bool `json:"IsDisabled"`
CommunicationStyle string `json:"CommunicationStyle"`
Environments []string `json:"Environments"`
Roles []string `json:"Roles"`
Expand Down Expand Up @@ -42,6 +43,7 @@ func GetDeploymentTargetAsJson(deps *cmd.Dependencies, target *machines.Deployme
Name: target.Name,
HealthStatus: target.HealthStatus,
StatusSummary: target.StatusSummary,
IsDisabled: target.IsDisabled,
CommunicationStyle: machinescommon.GetCommunicationStyle(target.Endpoint),
Environments: environments,
Roles: target.Roles,
Expand Down
2 changes: 2 additions & 0 deletions pkg/cmd/target/shared/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package shared

import (
"fmt"
"strconv"

"github.com/OctopusDeploy/cli/pkg/cmd"
"github.com/OctopusDeploy/cli/pkg/machinescommon"
Expand Down Expand Up @@ -51,6 +52,7 @@ func ViewRun(opts *ViewOptions, contributeEndpoint ContributeEndpointCallback, d
data = append(data, output.NewDataRow("Name", fmt.Sprintf("%s %s", output.Bold(target.Name), output.Dimf("(%s)", target.GetID()))))
data = append(data, output.NewDataRow("Health status", getHealthStatus(target)))
data = append(data, output.NewDataRow("Current status", target.StatusSummary))
data = append(data, output.NewDataRow("Disabled", strconv.FormatBool(target.IsDisabled)))

if contributeEndpoint != nil {
if machines.IsNil(target.Endpoint) {
Expand Down
60 changes: 60 additions & 0 deletions pkg/cmd/target/shared/view_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package shared_test

import (
"bytes"
"testing"

cmdRoot "github.com/OctopusDeploy/cli/pkg/cmd/root"
"github.com/OctopusDeploy/cli/pkg/question"
"github.com/OctopusDeploy/cli/test/fixtures"
"github.com/OctopusDeploy/cli/test/testutil"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/environments"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
)

var viewRootResource = testutil.NewRootResource()

// the per-type views all render through shared.ViewRun, so one of them is enough
// to cover the Disabled row it adds
func TestPerTypeViewShowsDisabledState(t *testing.T) {
const spaceID = "Spaces-1"
space1 := fixtures.NewSpace(spaceID, "Default Space")
development := fixtures.NewEnvironment(spaceID, "Environments-1", "Development")

for _, tc := range []struct {
name string
isDisabled bool
expected string
}{
{"disabled target", true, "true"},
{"enabled target", false, "false"},
} {
t.Run(tc.name, func(t *testing.T) {
stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
api, qa := testutil.NewMockServerAndAsker()
askProvider := question.NewAskProvider(qa.AsAsker())
fac := testutil.NewMockFactoryWithSpaceAndPrompt(api, space1, askProvider)
rootCmd := cmdRoot.NewCmdRoot(fac, nil, askProvider)
rootCmd.SetOut(stdout)
rootCmd.SetErr(stderr)

cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"deployment-target", "cloud-region", "view", "Machines-100", "--no-prompt"})
return rootCmd.ExecuteC()
})

api.ExpectRequest(t, "GET", "/api/").RespondWith(viewRootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1").RespondWith(viewRootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1/machines/Machines-100").
RespondWith(fixtures.NewDeploymentTarget(spaceID, "Machines-100", "web-server", tc.isDisabled))
api.ExpectRequest(t, "GET", "/api/Spaces-1/environments/all").RespondWith([]*environments.Environment{development})

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)
assert.Regexp(t, `Disabled\s+`+tc.expected, stdout.String())
assert.Equal(t, "", stderr.String())
})
}
}
8 changes: 7 additions & 1 deletion pkg/cmd/target/view/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package view

import (
"fmt"
"strconv"
"strings"

"github.com/MakeNowJust/heredoc/v2"
Expand Down Expand Up @@ -53,7 +54,7 @@ func ViewRun(opts *shared.ViewOptions) error {
return getDeploymentTargetAsJson(opts.Dependencies, t, environmentMap, tenantMap, workerPoolMap)
},
Table: output.TableDefinition[*machines.DeploymentTarget]{
Header: []string{"NAME", "TYPE", "HEALTH", "ENVIRONMENTS", "ROLES", "TENANTS", "TENANT TAGS", "ENDPOINT DETAILS", "DEFAULT WORKER POOL"},
Header: []string{"NAME", "TYPE", "HEALTH", "IS DISABLED", "ENVIRONMENTS", "ROLES", "TENANTS", "TENANT TAGS", "ENDPOINT DETAILS", "DEFAULT WORKER POOL"},
Row: func(t *machines.DeploymentTarget) []string {
return getDeploymentTargetAsTableRow(opts, t, environmentMap, tenantMap, workerPoolMap)
},
Expand All @@ -75,6 +76,7 @@ func getDeploymentTargetAsJson(deps *cmd.Dependencies, target *machines.Deployme
Name: target.Name,
HealthStatus: target.HealthStatus,
StatusSummary: target.StatusSummary,
IsDisabled: target.IsDisabled,
CommunicationStyle: machinescommon.GetCommunicationStyle(target.Endpoint),
Environments: environments,
Roles: target.Roles,
Expand Down Expand Up @@ -134,6 +136,7 @@ func getDeploymentTargetAsTableRow(opts *shared.ViewOptions, target *machines.De
output.Bold(target.Name),
targetType,
healthStatus,
strconv.FormatBool(target.IsDisabled),
strings.Join(environments, ", "),
strings.Join(target.Roles, ", "),
tenants,
Expand Down Expand Up @@ -198,6 +201,9 @@ func getDeploymentTargetAsBasic(opts *shared.ViewOptions, target *machines.Deplo
// Current status
result.WriteString(fmt.Sprintf("Current status: %s\n", target.StatusSummary))

// Disabled state
result.WriteString(fmt.Sprintf("Disabled: %s\n", strconv.FormatBool(target.IsDisabled)))

// Target type and endpoint details
targetType := getTargetTypeDisplayName(machinescommon.GetCommunicationStyle(target.Endpoint))
result.WriteString(fmt.Sprintf("Type: %s\n", output.Cyan(targetType)))
Expand Down
118 changes: 118 additions & 0 deletions pkg/cmd/target/view/view_disabled_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package view_test

import (
"bytes"
"testing"

cmdRoot "github.com/OctopusDeploy/cli/pkg/cmd/root"
"github.com/OctopusDeploy/cli/pkg/question"
"github.com/OctopusDeploy/cli/test/fixtures"
"github.com/OctopusDeploy/cli/test/testutil"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/environments"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/workerpools"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
)

const spaceID = "Spaces-1"

var development = fixtures.NewEnvironment(spaceID, "Environments-1", "Development")

func TestDeploymentTargetViewShowsDisabledState(t *testing.T) {
space1 := fixtures.NewSpace(spaceID, "Default Space")

tests := []struct {
name string
run func(t *testing.T, api *testutil.MockHttpServer, qa *testutil.AskMocker, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer)
}{
{"basic output reports a disabled target", func(t *testing.T, api *testutil.MockHttpServer, qa *testutil.AskMocker, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"deployment-target", "view", "Machines-100", "--no-prompt", "-f", "basic"})
return rootCmd.ExecuteC()
})

respondWithTarget(t, api, true)
// the basic renderer re-resolves the lookups it needs
api.ExpectRequest(t, "GET", "/api/Spaces-1/environments/all").RespondWith([]*environments.Environment{development})
api.ExpectRequest(t, "GET", "/api/Spaces-1/workerpools/all").RespondWith([]*workerpools.WorkerPoolListResult{})

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)
assert.Contains(t, stdOut.String(), "Disabled: true")
assert.Equal(t, "", stdErr.String())
}},

{"basic output reports an enabled target", func(t *testing.T, api *testutil.MockHttpServer, qa *testutil.AskMocker, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"deployment-target", "view", "Machines-100", "--no-prompt", "-f", "basic"})
return rootCmd.ExecuteC()
})

respondWithTarget(t, api, false)
api.ExpectRequest(t, "GET", "/api/Spaces-1/environments/all").RespondWith([]*environments.Environment{development})
api.ExpectRequest(t, "GET", "/api/Spaces-1/workerpools/all").RespondWith([]*workerpools.WorkerPoolListResult{})

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)
assert.Contains(t, stdOut.String(), "Disabled: false")
assert.Equal(t, "", stdErr.String())
}},

{"json output carries IsDisabled", func(t *testing.T, api *testutil.MockHttpServer, qa *testutil.AskMocker, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"deployment-target", "view", "Machines-100", "--no-prompt", "-f", "json"})
return rootCmd.ExecuteC()
})

respondWithTarget(t, api, true)

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)
assert.Contains(t, stdOut.String(), `"IsDisabled": true`)
assert.Equal(t, "", stdErr.String())
}},

{"table output has an IS DISABLED column", func(t *testing.T, api *testutil.MockHttpServer, qa *testutil.AskMocker, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"deployment-target", "view", "Machines-100", "--no-prompt", "-f", "table"})
return rootCmd.ExecuteC()
})

respondWithTarget(t, api, true)

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)
assert.Contains(t, stdOut.String(), "IS DISABLED")
assert.Regexp(t, `web-server.*true`, stdOut.String())
assert.Equal(t, "", stdErr.String())
}},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
api, qa := testutil.NewMockServerAndAsker()
askProvider := question.NewAskProvider(qa.AsAsker())
fac := testutil.NewMockFactoryWithSpaceAndPrompt(api, space1, askProvider)
rootCmd := cmdRoot.NewCmdRoot(fac, nil, askProvider)
rootCmd.SetOut(stdout)
rootCmd.SetErr(stderr)
test.run(t, api, qa, rootCmd, stdout, stderr)
})
}
}

func respondWithTarget(t *testing.T, api *testutil.MockHttpServer, isDisabled bool) {
api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1/machines/Machines-100").
RespondWith(fixtures.NewDeploymentTarget(spaceID, "Machines-100", "web-server", isDisabled))
api.ExpectRequest(t, "GET", "/api/Spaces-1/environments/all").RespondWith([]*environments.Environment{development})
api.ExpectRequest(t, "GET", "/api/Spaces-1/workerpools/all").RespondWith([]*workerpools.WorkerPoolListResult{})
api.ExpectRequest(t, "GET", "/api/Spaces-1/tenants/all").RespondWith([]*tenants.Tenant{})
}
Loading