From 79e2fdbfb4693eae72a8d2a43c945f1f4b3499e0 Mon Sep 17 00:00:00 2001 From: Shwetha Gururaj Date: Fri, 13 Dec 2024 11:13:55 -0500 Subject: [PATCH 1/4] Revision command implementation Co-authored-by: Lisa Burns Signed-off-by: Steve Taylor Update help info related to revision command Return error when no app/revision is present --- .github/workflows/golangci-lint.yml | 1 + actor/v7action/cloud_controller_client.go | 27 +- actor/v7action/revisions.go | 14 + actor/v7action/revisions_test.go | 79 +++++ .../fake_cloud_controller_client.go | 298 ++++++++++++++++++ .../ccv3/internal/api_routes.go | 50 +-- api/cloudcontroller/ccv3/revisions.go | 11 + api/cloudcontroller/ccv3/revisions_test.go | 76 +++++ command/common/help_command.go | 2 - command/common/internal/help_all_display.go | 11 +- .../common/internal/help_all_display_test.go | 10 - command/v7/actor.go | 1 + command/v7/revision_command.go | 120 ++++++- command/v7/revision_command_test.go | 223 ++++++++++++- command/v7/revisions_command.go | 7 +- command/v7/rollback_command.go | 2 +- command/v7/v7fakes/fake_actor.go | 89 ++++++ .../v7/isolated/revision_command_test.go | 127 +++++++- .../v7/isolated/revisions_command_test.go | 2 +- .../v7/isolated/rollback_command_test.go | 2 +- resources/metadata_resource.go | 3 +- resources/revision_resource.go | 16 +- 22 files changed, 1094 insertions(+), 77 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 7cd276f1f8a..5abe7e1adcd 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -20,6 +20,7 @@ on: - "doc/**" - ".gitpod.yml" - "README.md" + workflow_dispatch: jobs: golangci: diff --git a/actor/v7action/cloud_controller_client.go b/actor/v7action/cloud_controller_client.go index e8810085e4a..787a34f0d3c 100644 --- a/actor/v7action/cloud_controller_client.go +++ b/actor/v7action/cloud_controller_client.go @@ -14,6 +14,8 @@ import ( // CloudControllerClient is the interface to the cloud controller V3 API. type CloudControllerClient interface { + AppSSHEndpoint() string + AppSSHHostKeyFingerprint() string ApplyOrganizationQuota(quotaGUID string, orgGUID string) (resources.RelationshipList, ccv3.Warnings, error) ApplySpaceQuota(quotaGUID string, spaceGUID string) (resources.RelationshipList, ccv3.Warnings, error) CheckRoute(domainGUID string, hostname string, path string, port int) (bool, ccv3.Warnings, error) @@ -35,10 +37,10 @@ type CloudControllerClient interface { CreateRole(role resources.Role) (resources.Role, ccv3.Warnings, error) CreateRoute(route resources.Route) (resources.Route, ccv3.Warnings, error) CreateRouteBinding(binding resources.RouteBinding) (ccv3.JobURL, ccv3.Warnings, error) - CreateServiceBroker(serviceBroker resources.ServiceBroker) (ccv3.JobURL, ccv3.Warnings, error) CreateServiceCredentialBinding(binding resources.ServiceCredentialBinding) (ccv3.JobURL, ccv3.Warnings, error) CreateServiceInstance(serviceInstance resources.ServiceInstance) (ccv3.JobURL, ccv3.Warnings, error) CreateSecurityGroup(securityGroup resources.SecurityGroup) (resources.SecurityGroup, ccv3.Warnings, error) + CreateServiceBroker(serviceBroker resources.ServiceBroker) (ccv3.JobURL, ccv3.Warnings, error) CreateSpace(space resources.Space) (resources.Space, ccv3.Warnings, error) CreateSpaceQuota(spaceQuota resources.SpaceQuota) (resources.SpaceQuota, ccv3.Warnings, error) CreateUser(userGUID string) (resources.User, ccv3.Warnings, error) @@ -58,11 +60,12 @@ type CloudControllerClient interface { DeleteServiceCredentialBinding(guid string) (ccv3.JobURL, ccv3.Warnings, error) DeleteServiceBroker(serviceBrokerGUID string) (ccv3.JobURL, ccv3.Warnings, error) DeleteServiceInstance(serviceInstanceGUID string, query ...ccv3.Query) (ccv3.JobURL, ccv3.Warnings, error) - DeleteSpaceQuota(spaceQuotaGUID string) (ccv3.JobURL, ccv3.Warnings, error) DeleteSpace(guid string) (ccv3.JobURL, ccv3.Warnings, error) + DeleteSpaceQuota(spaceQuotaGUID string) (ccv3.JobURL, ccv3.Warnings, error) DeleteUser(userGUID string) (ccv3.JobURL, ccv3.Warnings, error) DownloadDroplet(dropletGUID string) ([]byte, ccv3.Warnings, error) EntitleIsolationSegmentToOrganizations(isoGUID string, orgGUIDs []string) (resources.RelationshipList, ccv3.Warnings, error) + GetAppFeature(appGUID string, featureName string) (resources.ApplicationFeature, ccv3.Warnings, error) GetApplicationByNameAndSpace(appName string, spaceGUID string) (resources.Application, ccv3.Warnings, error) GetApplicationDropletCurrent(appGUID string) (resources.Droplet, ccv3.Warnings, error) GetApplicationEnvironment(appGUID string) (ccv3.Environment, ccv3.Warnings, error) @@ -84,6 +87,7 @@ type CloudControllerClient interface { GetDroplet(guid string) (resources.Droplet, ccv3.Warnings, error) GetDroplets(query ...ccv3.Query) ([]resources.Droplet, ccv3.Warnings, error) GetEnvironmentVariableGroup(group constant.EnvironmentVariableGroupName) (resources.EnvironmentVariables, ccv3.Warnings, error) + GetEnvironmentVariablesByURL(url string) (resources.EnvironmentVariables, ccv3.Warnings, error) GetEvents(query ...ccv3.Query) ([]ccv3.Event, ccv3.Warnings, error) GetFeatureFlag(featureFlagName string) (resources.FeatureFlag, ccv3.Warnings, error) GetFeatureFlags() ([]resources.FeatureFlag, ccv3.Warnings, error) @@ -99,17 +103,18 @@ type CloudControllerClient interface { GetOrganizationQuotas(query ...ccv3.Query) ([]resources.OrganizationQuota, ccv3.Warnings, error) GetOrganizations(query ...ccv3.Query) ([]resources.Organization, ccv3.Warnings, error) GetPackage(guid string) (resources.Package, ccv3.Warnings, error) - GetPackages(query ...ccv3.Query) ([]resources.Package, ccv3.Warnings, error) GetPackageDroplets(packageGUID string, query ...ccv3.Query) ([]resources.Droplet, ccv3.Warnings, error) + GetPackages(query ...ccv3.Query) ([]resources.Package, ccv3.Warnings, error) GetProcess(processGUID string) (resources.Process, ccv3.Warnings, error) - GetProcesses(query ...ccv3.Query) ([]resources.Process, ccv3.Warnings, error) GetProcessInstances(processGUID string) ([]ccv3.ProcessInstance, ccv3.Warnings, error) GetProcessSidecars(processGUID string) ([]resources.Sidecar, ccv3.Warnings, error) + GetProcesses(query ...ccv3.Query) ([]resources.Process, ccv3.Warnings, error) GetRoles(query ...ccv3.Query) ([]resources.Role, ccv3.IncludedResources, ccv3.Warnings, error) GetRouteBindings(query ...ccv3.Query) ([]resources.RouteBinding, ccv3.IncludedResources, ccv3.Warnings, error) GetRouteDestinations(routeGUID string) ([]resources.RouteDestination, ccv3.Warnings, error) GetRoutes(query ...ccv3.Query) ([]resources.Route, ccv3.Warnings, error) GetRunningSecurityGroups(spaceGUID string, queries ...ccv3.Query) ([]resources.SecurityGroup, ccv3.Warnings, error) + GetSSHEnabled(appGUID string) (ccv3.SSHEnabled, ccv3.Warnings, error) GetSecurityGroups(query ...ccv3.Query) ([]resources.SecurityGroup, ccv3.Warnings, error) GetServiceBrokers(query ...ccv3.Query) ([]resources.ServiceBroker, ccv3.Warnings, error) GetServiceCredentialBindings(query ...ccv3.Query) ([]resources.ServiceCredentialBinding, ccv3.Warnings, error) @@ -120,9 +125,9 @@ type CloudControllerClient interface { GetServiceInstanceUsageSummary(serviceInstanceGUID string) ([]resources.ServiceInstanceUsageSummary, ccv3.Warnings, error) GetServiceInstances(query ...ccv3.Query) ([]resources.ServiceInstance, ccv3.IncludedResources, ccv3.Warnings, error) GetServiceOfferingByGUID(guid string) (resources.ServiceOffering, ccv3.Warnings, error) - GetServiceOfferings(query ...ccv3.Query) ([]resources.ServiceOffering, ccv3.Warnings, error) GetServiceOfferingByNameAndBroker(serviceOfferingName, serviceBrokerName string) (resources.ServiceOffering, ccv3.Warnings, error) GetServicePlanByGUID(guid string) (resources.ServicePlan, ccv3.Warnings, error) + GetServiceOfferings(query ...ccv3.Query) ([]resources.ServiceOffering, ccv3.Warnings, error) GetServicePlans(query ...ccv3.Query) ([]resources.ServicePlan, ccv3.Warnings, error) GetServicePlansWithOfferings(query ...ccv3.Query) ([]ccv3.ServiceOfferingWithPlans, ccv3.Warnings, error) GetServicePlansWithSpaceAndOrganization(query ...ccv3.Query) ([]ccv3.ServicePlanWithSpaceAndOrganization, ccv3.Warnings, error) @@ -130,10 +135,8 @@ type CloudControllerClient interface { GetSpaceIsolationSegment(spaceGUID string) (resources.Relationship, ccv3.Warnings, error) GetSpaceManifestDiff(spaceGUID string, rawManifest []byte) (resources.ManifestDiff, ccv3.Warnings, error) GetSpaceQuota(spaceQuotaGUID string) (resources.SpaceQuota, ccv3.Warnings, error) - GetSpaces(query ...ccv3.Query) ([]resources.Space, ccv3.IncludedResources, ccv3.Warnings, error) GetSpaceQuotas(query ...ccv3.Query) ([]resources.SpaceQuota, ccv3.Warnings, error) - GetSSHEnabled(appGUID string) (ccv3.SSHEnabled, ccv3.Warnings, error) - GetAppFeature(appGUID string, featureName string) (resources.ApplicationFeature, ccv3.Warnings, error) + GetSpaces(query ...ccv3.Query) ([]resources.Space, ccv3.IncludedResources, ccv3.Warnings, error) GetStacks(query ...ccv3.Query) ([]resources.Stack, ccv3.Warnings, error) GetStagingSecurityGroups(spaceGUID string, queries ...ccv3.Query) ([]resources.SecurityGroup, ccv3.Warnings, error) GetTask(guid string) (resources.Task, ccv3.Warnings, error) @@ -157,6 +160,7 @@ type CloudControllerClient interface { UnbindSecurityGroupStagingSpace(securityGroupGUID string, spaceGUID string) (ccv3.Warnings, error) UnmapRoute(routeGUID string, destinationGUID string) (ccv3.Warnings, error) UnshareRoute(routeGUID string, spaceGUID string) (ccv3.Warnings, error) + UnsetSpaceQuota(spaceQuotaGUID, spaceGUID string) (ccv3.Warnings, error) UnsharePrivateDomainFromOrg(domainGUID string, sharedOrgGUID string) (ccv3.Warnings, error) UnshareServiceInstanceFromSpace(serviceInstanceGUID string, sharedToSpaceGUID string) (ccv3.Warnings, error) UpdateAppFeature(appGUID string, enabled bool, featureName string) (ccv3.Warnings, error) @@ -176,17 +180,16 @@ type CloudControllerClient interface { UpdateOrganizationQuota(orgQuota resources.OrganizationQuota) (resources.OrganizationQuota, ccv3.Warnings, error) UpdateProcess(process resources.Process) (resources.Process, ccv3.Warnings, error) UpdateResourceMetadata(resource string, resourceGUID string, metadata resources.Metadata) (ccv3.JobURL, ccv3.Warnings, error) + UpdateSecurityGroup(securityGroup resources.SecurityGroup) (resources.SecurityGroup, ccv3.Warnings, error) UpdateSecurityGroupRunningSpace(securityGroupGUID string, spaceGUIDs []string) (ccv3.Warnings, error) UpdateSecurityGroupStagingSpace(securityGroupGUID string, spaceGUIDs []string) (ccv3.Warnings, error) - UpdateSecurityGroup(securityGroup resources.SecurityGroup) (resources.SecurityGroup, ccv3.Warnings, error) + UpdateServiceBroker(serviceBrokerGUID string, serviceBroker resources.ServiceBroker) (ccv3.JobURL, ccv3.Warnings, error) UpdateServiceInstance(serviceInstanceGUID string, serviceInstanceUpdates resources.ServiceInstance) (ccv3.JobURL, ccv3.Warnings, error) UpdateSpace(space resources.Space) (resources.Space, ccv3.Warnings, error) UpdateSpaceApplyManifest(spaceGUID string, rawManifest []byte) (ccv3.JobURL, ccv3.Warnings, error) UpdateSpaceFeature(spaceGUID string, enabled bool, featureName string) (ccv3.Warnings, error) UpdateSpaceIsolationSegmentRelationship(spaceGUID string, isolationSegmentGUID string) (resources.Relationship, ccv3.Warnings, error) UpdateSpaceQuota(spaceQuota resources.SpaceQuota) (resources.SpaceQuota, ccv3.Warnings, error) - UnsetSpaceQuota(spaceQuotaGUID, spaceGUID string) (ccv3.Warnings, error) - UpdateServiceBroker(serviceBrokerGUID string, serviceBroker resources.ServiceBroker) (ccv3.JobURL, ccv3.Warnings, error) UpdateTaskCancel(taskGUID string) (resources.Task, ccv3.Warnings, error) UploadBitsPackage(pkg resources.Package, matchedResources []ccv3.Resource, newResources io.Reader, newResourcesLength int64) (resources.Package, ccv3.Warnings, error) UploadBuildpack(buildpackGUID string, buildpackPath string, buildpack io.Reader, buildpackLength int64) (ccv3.JobURL, ccv3.Warnings, error) @@ -198,9 +201,9 @@ type CloudControllerClient interface { } type servicePlanVisibilityClient interface { + DeleteServicePlanVisibility(servicePlanGUID, organizationGUID string) (ccv3.Warnings, error) GetServicePlanVisibility(servicePlanGUID string) (resources.ServicePlanVisibility, ccv3.Warnings, error) UpdateServicePlanVisibility(servicePlanGUID string, visibility resources.ServicePlanVisibility) (resources.ServicePlanVisibility, ccv3.Warnings, error) - DeleteServicePlanVisibility(servicePlanGUID, organizationGUID string) (ccv3.Warnings, error) } // TODO: Split this enormous interface diff --git a/actor/v7action/revisions.go b/actor/v7action/revisions.go index c2109c8f95a..f159ab64364 100644 --- a/actor/v7action/revisions.go +++ b/actor/v7action/revisions.go @@ -76,6 +76,20 @@ func (actor Actor) GetRevisionByApplicationAndVersion(appGUID string, revisionVe return revisions[0], Warnings(warnings), nil } +func (actor Actor) GetEnvironmentVariableGroupByRevision(revision resources.Revision) (EnvironmentVariableGroup, bool, Warnings, error) { + envVarApiLink, isPresent := revision.Links["environment_variables"] + if !isPresent { + return EnvironmentVariableGroup{}, isPresent, Warnings{"Unable to retrieve environment variables for revision."}, nil + } + + environmentVariables, warnings, err := actor.CloudControllerClient.GetEnvironmentVariablesByURL(envVarApiLink.HREF) + if err != nil { + return EnvironmentVariableGroup{}, false, Warnings(warnings), err + } + + return EnvironmentVariableGroup(environmentVariables), true, Warnings(warnings), nil +} + func (actor Actor) setRevisionsDeployableByDropletStateForApp(appGUID string, revisions []resources.Revision) ([]resources.Revision, Warnings, error) { droplets, warnings, err := actor.CloudControllerClient.GetDroplets( ccv3.Query{Key: ccv3.AppGUIDFilter, Values: []string{appGUID}}, diff --git a/actor/v7action/revisions_test.go b/actor/v7action/revisions_test.go index 61e8b96eaba..2a8c74368d4 100644 --- a/actor/v7action/revisions_test.go +++ b/actor/v7action/revisions_test.go @@ -5,11 +5,13 @@ import ( "strconv" "code.cloudfoundry.org/cli/actor/actionerror" + "code.cloudfoundry.org/cli/actor/v7action" . "code.cloudfoundry.org/cli/actor/v7action" "code.cloudfoundry.org/cli/actor/v7action/v7actionfakes" "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" "code.cloudfoundry.org/cli/resources" + "code.cloudfoundry.org/cli/types" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) @@ -397,4 +399,81 @@ var _ = Describe("Revisions Actions", func() { }) }) }) + + Describe("GetEnvironmentVariableGroupByRevision", func() { + var ( + actor *Actor + environmentVariablesGroup v7action.EnvironmentVariableGroup + executeErr error + fakeCloudControllerClient *v7actionfakes.FakeCloudControllerClient + fakeConfig *v7actionfakes.FakeConfig + isPresent bool + revision resources.Revision + warnings Warnings + ) + + BeforeEach(func() { + fakeCloudControllerClient = new(v7actionfakes.FakeCloudControllerClient) + fakeConfig = new(v7actionfakes.FakeConfig) + actor = NewActor(fakeCloudControllerClient, fakeConfig, nil, nil, nil, nil) + revision = resources.Revision{ + Links: resources.APILinks{ + "environment_variables": resources.APILink{ + HREF: "url", + }, + }, + } + fakeConfig.APIVersionReturns("3.86.0") + }) + + JustBeforeEach(func() { + environmentVariablesGroup, isPresent, warnings, executeErr = actor.GetEnvironmentVariableGroupByRevision(revision) + }) + + When("the revision does not provide HREF", func() { + BeforeEach(func() { + revision = resources.Revision{} + }) + + It("returns as not present", func() { + Expect(executeErr).To(Not(HaveOccurred())) + Expect(warnings).To(ConsistOf("Unable to retrieve environment variables for revision.")) + Expect(isPresent).To(Equal(false)) + }) + }) + + When("finding the environment variables fails", func() { + BeforeEach(func() { + fakeCloudControllerClient.GetEnvironmentVariablesByURLReturns( + nil, + ccv3.Warnings{"get-env-vars-warning-1"}, + errors.New("get-env-vars-error-1"), + ) + }) + + It("returns an error and warnings", func() { + Expect(executeErr).To(MatchError("get-env-vars-error-1")) + Expect(warnings).To(ConsistOf("get-env-vars-warning-1")) + }) + }) + + When("finding the environment variables succeeds", func() { + BeforeEach(func() { + fakeCloudControllerClient.GetEnvironmentVariablesByURLReturns( + resources.EnvironmentVariables{"foo": *types.NewFilteredString("bar")}, + ccv3.Warnings{"get-env-vars-warning-1"}, + nil, + ) + }) + + It("returns the environment variables and warnings", func() { + Expect(executeErr).ToNot(HaveOccurred()) + Expect(fakeCloudControllerClient.GetEnvironmentVariablesByURLCallCount()).To(Equal(1)) + Expect(fakeCloudControllerClient.GetEnvironmentVariablesByURLArgsForCall(0)).To(Equal("url")) + Expect(warnings).To(ConsistOf("get-env-vars-warning-1")) + Expect(len(environmentVariablesGroup)).To(Equal(1)) + Expect(environmentVariablesGroup["foo"].Value).To(Equal("bar")) + }) + }) + }) }) diff --git a/actor/v7action/v7actionfakes/fake_cloud_controller_client.go b/actor/v7action/v7actionfakes/fake_cloud_controller_client.go index 942728edbda..666b5a40695 100644 --- a/actor/v7action/v7actionfakes/fake_cloud_controller_client.go +++ b/actor/v7action/v7actionfakes/fake_cloud_controller_client.go @@ -14,6 +14,26 @@ import ( ) type FakeCloudControllerClient struct { + AppSSHEndpointStub func() string + appSSHEndpointMutex sync.RWMutex + appSSHEndpointArgsForCall []struct { + } + appSSHEndpointReturns struct { + result1 string + } + appSSHEndpointReturnsOnCall map[int]struct { + result1 string + } + AppSSHHostKeyFingerprintStub func() string + appSSHHostKeyFingerprintMutex sync.RWMutex + appSSHHostKeyFingerprintArgsForCall []struct { + } + appSSHHostKeyFingerprintReturns struct { + result1 string + } + appSSHHostKeyFingerprintReturnsOnCall map[int]struct { + result1 string + } ApplyOrganizationQuotaStub func(string, string) (resources.RelationshipList, ccv3.Warnings, error) applyOrganizationQuotaMutex sync.RWMutex applyOrganizationQuotaArgsForCall []struct { @@ -1101,6 +1121,21 @@ type FakeCloudControllerClient struct { result2 ccv3.Warnings result3 error } + GetEnvironmentVariablesByURLStub func(string) (resources.EnvironmentVariables, ccv3.Warnings, error) + getEnvironmentVariablesByURLMutex sync.RWMutex + getEnvironmentVariablesByURLArgsForCall []struct { + arg1 string + } + getEnvironmentVariablesByURLReturns struct { + result1 resources.EnvironmentVariables + result2 ccv3.Warnings + result3 error + } + getEnvironmentVariablesByURLReturnsOnCall map[int]struct { + result1 resources.EnvironmentVariables + result2 ccv3.Warnings + result3 error + } GetEventsStub func(...ccv3.Query) ([]ccv3.Event, ccv3.Warnings, error) getEventsMutex sync.RWMutex getEventsArgsForCall []struct { @@ -1417,6 +1452,21 @@ type FakeCloudControllerClient struct { result2 ccv3.Warnings result3 error } + GetRevisionStub func(string) (resources.Revision, ccv3.Warnings, error) + getRevisionMutex sync.RWMutex + getRevisionArgsForCall []struct { + arg1 string + } + getRevisionReturns struct { + result1 resources.Revision + result2 ccv3.Warnings + result3 error + } + getRevisionReturnsOnCall map[int]struct { + result1 resources.Revision + result2 ccv3.Warnings + result3 error + } GetRolesStub func(...ccv3.Query) ([]resources.Role, ccv3.IncludedResources, ccv3.Warnings, error) getRolesMutex sync.RWMutex getRolesArgsForCall []struct { @@ -2767,6 +2817,112 @@ type FakeCloudControllerClient struct { invocationsMutex sync.RWMutex } +func (fake *FakeCloudControllerClient) AppSSHEndpoint() string { + fake.appSSHEndpointMutex.Lock() + ret, specificReturn := fake.appSSHEndpointReturnsOnCall[len(fake.appSSHEndpointArgsForCall)] + fake.appSSHEndpointArgsForCall = append(fake.appSSHEndpointArgsForCall, struct { + }{}) + stub := fake.AppSSHEndpointStub + fakeReturns := fake.appSSHEndpointReturns + fake.recordInvocation("AppSSHEndpoint", []interface{}{}) + fake.appSSHEndpointMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeCloudControllerClient) AppSSHEndpointCallCount() int { + fake.appSSHEndpointMutex.RLock() + defer fake.appSSHEndpointMutex.RUnlock() + return len(fake.appSSHEndpointArgsForCall) +} + +func (fake *FakeCloudControllerClient) AppSSHEndpointCalls(stub func() string) { + fake.appSSHEndpointMutex.Lock() + defer fake.appSSHEndpointMutex.Unlock() + fake.AppSSHEndpointStub = stub +} + +func (fake *FakeCloudControllerClient) AppSSHEndpointReturns(result1 string) { + fake.appSSHEndpointMutex.Lock() + defer fake.appSSHEndpointMutex.Unlock() + fake.AppSSHEndpointStub = nil + fake.appSSHEndpointReturns = struct { + result1 string + }{result1} +} + +func (fake *FakeCloudControllerClient) AppSSHEndpointReturnsOnCall(i int, result1 string) { + fake.appSSHEndpointMutex.Lock() + defer fake.appSSHEndpointMutex.Unlock() + fake.AppSSHEndpointStub = nil + if fake.appSSHEndpointReturnsOnCall == nil { + fake.appSSHEndpointReturnsOnCall = make(map[int]struct { + result1 string + }) + } + fake.appSSHEndpointReturnsOnCall[i] = struct { + result1 string + }{result1} +} + +func (fake *FakeCloudControllerClient) AppSSHHostKeyFingerprint() string { + fake.appSSHHostKeyFingerprintMutex.Lock() + ret, specificReturn := fake.appSSHHostKeyFingerprintReturnsOnCall[len(fake.appSSHHostKeyFingerprintArgsForCall)] + fake.appSSHHostKeyFingerprintArgsForCall = append(fake.appSSHHostKeyFingerprintArgsForCall, struct { + }{}) + stub := fake.AppSSHHostKeyFingerprintStub + fakeReturns := fake.appSSHHostKeyFingerprintReturns + fake.recordInvocation("AppSSHHostKeyFingerprint", []interface{}{}) + fake.appSSHHostKeyFingerprintMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeCloudControllerClient) AppSSHHostKeyFingerprintCallCount() int { + fake.appSSHHostKeyFingerprintMutex.RLock() + defer fake.appSSHHostKeyFingerprintMutex.RUnlock() + return len(fake.appSSHHostKeyFingerprintArgsForCall) +} + +func (fake *FakeCloudControllerClient) AppSSHHostKeyFingerprintCalls(stub func() string) { + fake.appSSHHostKeyFingerprintMutex.Lock() + defer fake.appSSHHostKeyFingerprintMutex.Unlock() + fake.AppSSHHostKeyFingerprintStub = stub +} + +func (fake *FakeCloudControllerClient) AppSSHHostKeyFingerprintReturns(result1 string) { + fake.appSSHHostKeyFingerprintMutex.Lock() + defer fake.appSSHHostKeyFingerprintMutex.Unlock() + fake.AppSSHHostKeyFingerprintStub = nil + fake.appSSHHostKeyFingerprintReturns = struct { + result1 string + }{result1} +} + +func (fake *FakeCloudControllerClient) AppSSHHostKeyFingerprintReturnsOnCall(i int, result1 string) { + fake.appSSHHostKeyFingerprintMutex.Lock() + defer fake.appSSHHostKeyFingerprintMutex.Unlock() + fake.AppSSHHostKeyFingerprintStub = nil + if fake.appSSHHostKeyFingerprintReturnsOnCall == nil { + fake.appSSHHostKeyFingerprintReturnsOnCall = make(map[int]struct { + result1 string + }) + } + fake.appSSHHostKeyFingerprintReturnsOnCall[i] = struct { + result1 string + }{result1} +} + func (fake *FakeCloudControllerClient) ApplyOrganizationQuota(arg1 string, arg2 string) (resources.RelationshipList, ccv3.Warnings, error) { fake.applyOrganizationQuotaMutex.Lock() ret, specificReturn := fake.applyOrganizationQuotaReturnsOnCall[len(fake.applyOrganizationQuotaArgsForCall)] @@ -7597,6 +7753,73 @@ func (fake *FakeCloudControllerClient) GetEnvironmentVariableGroupReturnsOnCall( }{result1, result2, result3} } +func (fake *FakeCloudControllerClient) GetEnvironmentVariablesByURL(arg1 string) (resources.EnvironmentVariables, ccv3.Warnings, error) { + fake.getEnvironmentVariablesByURLMutex.Lock() + ret, specificReturn := fake.getEnvironmentVariablesByURLReturnsOnCall[len(fake.getEnvironmentVariablesByURLArgsForCall)] + fake.getEnvironmentVariablesByURLArgsForCall = append(fake.getEnvironmentVariablesByURLArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.GetEnvironmentVariablesByURLStub + fakeReturns := fake.getEnvironmentVariablesByURLReturns + fake.recordInvocation("GetEnvironmentVariablesByURL", []interface{}{arg1}) + fake.getEnvironmentVariablesByURLMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2, ret.result3 + } + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 +} + +func (fake *FakeCloudControllerClient) GetEnvironmentVariablesByURLCallCount() int { + fake.getEnvironmentVariablesByURLMutex.RLock() + defer fake.getEnvironmentVariablesByURLMutex.RUnlock() + return len(fake.getEnvironmentVariablesByURLArgsForCall) +} + +func (fake *FakeCloudControllerClient) GetEnvironmentVariablesByURLCalls(stub func(string) (resources.EnvironmentVariables, ccv3.Warnings, error)) { + fake.getEnvironmentVariablesByURLMutex.Lock() + defer fake.getEnvironmentVariablesByURLMutex.Unlock() + fake.GetEnvironmentVariablesByURLStub = stub +} + +func (fake *FakeCloudControllerClient) GetEnvironmentVariablesByURLArgsForCall(i int) string { + fake.getEnvironmentVariablesByURLMutex.RLock() + defer fake.getEnvironmentVariablesByURLMutex.RUnlock() + argsForCall := fake.getEnvironmentVariablesByURLArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeCloudControllerClient) GetEnvironmentVariablesByURLReturns(result1 resources.EnvironmentVariables, result2 ccv3.Warnings, result3 error) { + fake.getEnvironmentVariablesByURLMutex.Lock() + defer fake.getEnvironmentVariablesByURLMutex.Unlock() + fake.GetEnvironmentVariablesByURLStub = nil + fake.getEnvironmentVariablesByURLReturns = struct { + result1 resources.EnvironmentVariables + result2 ccv3.Warnings + result3 error + }{result1, result2, result3} +} + +func (fake *FakeCloudControllerClient) GetEnvironmentVariablesByURLReturnsOnCall(i int, result1 resources.EnvironmentVariables, result2 ccv3.Warnings, result3 error) { + fake.getEnvironmentVariablesByURLMutex.Lock() + defer fake.getEnvironmentVariablesByURLMutex.Unlock() + fake.GetEnvironmentVariablesByURLStub = nil + if fake.getEnvironmentVariablesByURLReturnsOnCall == nil { + fake.getEnvironmentVariablesByURLReturnsOnCall = make(map[int]struct { + result1 resources.EnvironmentVariables + result2 ccv3.Warnings + result3 error + }) + } + fake.getEnvironmentVariablesByURLReturnsOnCall[i] = struct { + result1 resources.EnvironmentVariables + result2 ccv3.Warnings + result3 error + }{result1, result2, result3} +} + func (fake *FakeCloudControllerClient) GetEvents(arg1 ...ccv3.Query) ([]ccv3.Event, ccv3.Warnings, error) { fake.getEventsMutex.Lock() ret, specificReturn := fake.getEventsReturnsOnCall[len(fake.getEventsArgsForCall)] @@ -8991,6 +9214,73 @@ func (fake *FakeCloudControllerClient) GetProcessesReturnsOnCall(i int, result1 }{result1, result2, result3} } +func (fake *FakeCloudControllerClient) GetRevision(arg1 string) (resources.Revision, ccv3.Warnings, error) { + fake.getRevisionMutex.Lock() + ret, specificReturn := fake.getRevisionReturnsOnCall[len(fake.getRevisionArgsForCall)] + fake.getRevisionArgsForCall = append(fake.getRevisionArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.GetRevisionStub + fakeReturns := fake.getRevisionReturns + fake.recordInvocation("GetRevision", []interface{}{arg1}) + fake.getRevisionMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2, ret.result3 + } + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 +} + +func (fake *FakeCloudControllerClient) GetRevisionCallCount() int { + fake.getRevisionMutex.RLock() + defer fake.getRevisionMutex.RUnlock() + return len(fake.getRevisionArgsForCall) +} + +func (fake *FakeCloudControllerClient) GetRevisionCalls(stub func(string) (resources.Revision, ccv3.Warnings, error)) { + fake.getRevisionMutex.Lock() + defer fake.getRevisionMutex.Unlock() + fake.GetRevisionStub = stub +} + +func (fake *FakeCloudControllerClient) GetRevisionArgsForCall(i int) string { + fake.getRevisionMutex.RLock() + defer fake.getRevisionMutex.RUnlock() + argsForCall := fake.getRevisionArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeCloudControllerClient) GetRevisionReturns(result1 resources.Revision, result2 ccv3.Warnings, result3 error) { + fake.getRevisionMutex.Lock() + defer fake.getRevisionMutex.Unlock() + fake.GetRevisionStub = nil + fake.getRevisionReturns = struct { + result1 resources.Revision + result2 ccv3.Warnings + result3 error + }{result1, result2, result3} +} + +func (fake *FakeCloudControllerClient) GetRevisionReturnsOnCall(i int, result1 resources.Revision, result2 ccv3.Warnings, result3 error) { + fake.getRevisionMutex.Lock() + defer fake.getRevisionMutex.Unlock() + fake.GetRevisionStub = nil + if fake.getRevisionReturnsOnCall == nil { + fake.getRevisionReturnsOnCall = make(map[int]struct { + result1 resources.Revision + result2 ccv3.Warnings + result3 error + }) + } + fake.getRevisionReturnsOnCall[i] = struct { + result1 resources.Revision + result2 ccv3.Warnings + result3 error + }{result1, result2, result3} +} + func (fake *FakeCloudControllerClient) GetRoles(arg1 ...ccv3.Query) ([]resources.Role, ccv3.IncludedResources, ccv3.Warnings, error) { fake.getRolesMutex.Lock() ret, specificReturn := fake.getRolesReturnsOnCall[len(fake.getRolesArgsForCall)] @@ -14958,6 +15248,10 @@ func (fake *FakeCloudControllerClient) WhoAmIReturnsOnCall(i int, result1 resour func (fake *FakeCloudControllerClient) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() + fake.appSSHEndpointMutex.RLock() + defer fake.appSSHEndpointMutex.RUnlock() + fake.appSSHHostKeyFingerprintMutex.RLock() + defer fake.appSSHHostKeyFingerprintMutex.RUnlock() fake.applyOrganizationQuotaMutex.RLock() defer fake.applyOrganizationQuotaMutex.RUnlock() fake.applySpaceQuotaMutex.RLock() @@ -15102,6 +15396,8 @@ func (fake *FakeCloudControllerClient) Invocations() map[string][][]interface{} defer fake.getDropletsMutex.RUnlock() fake.getEnvironmentVariableGroupMutex.RLock() defer fake.getEnvironmentVariableGroupMutex.RUnlock() + fake.getEnvironmentVariablesByURLMutex.RLock() + defer fake.getEnvironmentVariablesByURLMutex.RUnlock() fake.getEventsMutex.RLock() defer fake.getEventsMutex.RUnlock() fake.getFeatureFlagMutex.RLock() @@ -15144,6 +15440,8 @@ func (fake *FakeCloudControllerClient) Invocations() map[string][][]interface{} defer fake.getProcessSidecarsMutex.RUnlock() fake.getProcessesMutex.RLock() defer fake.getProcessesMutex.RUnlock() + fake.getRevisionMutex.RLock() + defer fake.getRevisionMutex.RUnlock() fake.getRolesMutex.RLock() defer fake.getRolesMutex.RUnlock() fake.getRouteBindingsMutex.RLock() diff --git a/api/cloudcontroller/ccv3/internal/api_routes.go b/api/cloudcontroller/ccv3/internal/api_routes.go index 83d740b56af..76a86b269c6 100644 --- a/api/cloudcontroller/ccv3/internal/api_routes.go +++ b/api/cloudcontroller/ccv3/internal/api_routes.go @@ -15,15 +15,15 @@ const ( DeleteDomainRequest = "DeleteDomainRequest" DeleteIsolationSegmentRelationshipOrganizationRequest = "DeleteIsolationSegmentRelationshipOrganization" DeleteIsolationSegmentRequest = "DeleteIsolationSegment" - DeleteOrganizationRequest = "DeleteOrganization" DeleteOrganizationQuotaRequest = "DeleteOrganizationQuota" + DeleteOrganizationRequest = "DeleteOrganization" DeleteOrphanedRoutesRequest = "DeleteOrphanedRoutes" DeleteRoleRequest = "DeleteRoleRequest" DeleteRouteRequest = "DeleteRouteRequest" DeleteRouteBindingRequest = "DeleteRouteBinding" DeleteSecurityGroupRequest = "DeleteSecurityGroup" - DeleteSecurityGroupStagingSpaceRequest = "DeleteSecurityGroupStagingSpace" DeleteSecurityGroupRunningSpaceRequest = "DeleteSecurityGroupRunningSpace" + DeleteSecurityGroupStagingSpaceRequest = "DeleteSecurityGroupStagingSpace" DeleteServiceCredentialBindingRequest = "DeleteServiceCredentialBinding" DeleteServiceBrokerRequest = "DeleteServiceBrokerRequest" DeleteServiceInstanceRelationshipsSharedSpaceRequest = "DeleteServiceInstanceRelationshipsSharedSpace" @@ -31,9 +31,9 @@ const ( DeleteServiceOfferingRequest = "DeleteServiceOffering" DeleteServicePlanVisibilityRequest = "DeleteServicePlanVisibility" DeleteSharedOrgFromDomainRequest = "DeleteSharedOrgFromDomain" + DeleteSpaceQuotaFromSpaceRequest = "DeleteSpaceQuotaFromSpace" DeleteSpaceQuotaRequest = "DeleteSpaceQuota" DeleteSpaceRequest = "DeleteSpace" - DeleteSpaceQuotaFromSpaceRequest = "DeleteSpaceQuotaFromSpace" DeleteUserRequest = "DeleteUser" GetApplicationDropletCurrentRequest = "GetApplicationDropletCurrent" GetApplicationEnvRequest = "GetApplicationEnv" @@ -41,8 +41,8 @@ const ( GetApplicationManifestRequest = "GetApplicationManifest" GetApplicationProcessRequest = "GetApplicationProcess" GetApplicationProcessesRequest = "GetApplicationProcesses" - GetApplicationRevisionsRequest = "GetApplicationRevisions" GetApplicationRevisionsDeployedRequest = "GetApplicationRevisionsDeployed" + GetApplicationRevisionsRequest = "GetApplicationRevisions" GetApplicationRoutesRequest = "GetApplicationRoutes" GetApplicationTasksRequest = "GetApplicationTasks" GetApplicationsRequest = "GetApplications" @@ -54,9 +54,9 @@ const ( GetDomainRequest = "GetDomain" GetDomainRouteReservationsRequest = "GetDomainRouteReservations" GetDomainsRequest = "GetDomains" + GetDropletBitsRequest = "GetDropletBits" GetDropletRequest = "GetDroplet" GetDropletsRequest = "GetDroplets" - GetDropletBitsRequest = "GetDropletBits" GetEnvironmentVariableGroupRequest = "GetEnvironmentVariableGroup" GetEventsRequest = "GetEvents" GetFeatureFlagRequest = "GetFeatureFlag" @@ -65,22 +65,23 @@ const ( GetIsolationSegmentRequest = "GetIsolationSegment" GetIsolationSegmentsRequest = "GetIsolationSegments" GetOrganizationDomainsRequest = "GetOrganizationDomains" - GetOrganizationQuotasRequest = "GetOrganizationQuotas" GetOrganizationQuotaRequest = "GetOrganizationQuota" + GetOrganizationQuotasRequest = "GetOrganizationQuotas" GetOrganizationRelationshipDefaultIsolationSegmentRequest = "GetOrganizationRelationshipDefaultIsolationSegment" GetOrganizationRequest = "GetOrganization" GetOrganizationsRequest = "GetOrganizations" + GetPackageDropletsRequest = "GetPackageDroplets" GetPackageRequest = "GetPackage" GetPackagesRequest = "GetPackages" - GetPackageDropletsRequest = "GetPackageDroplets" GetProcessRequest = "GetProcess" - GetProcessesRequest = "GetProcesses" - GetProcessStatsRequest = "GetProcessStats" GetProcessSidecarsRequest = "GetProcessSidecars" + GetProcessStatsRequest = "GetProcessStats" + GetProcessesRequest = "GetProcesses" GetRolesRequest = "GetRoles" GetRouteBindingsRequest = "GetRouteBindings" GetRouteDestinationsRequest = "GetRouteDestinations" GetRoutesRequest = "GetRoutes" + GetSSHEnabled = "GetSSHEnabled" GetSecurityGroupsRequest = "GetSecurityGroups" GetServiceBrokersRequest = "GetServiceBrokers" GetServiceCredentialBindingsRequest = "GetServiceCredentialBindings" @@ -92,16 +93,15 @@ const ( GetServiceOfferingRequest = "GetServiceOffering" GetServiceOfferingsRequest = "GetServiceOfferings" GetServicePlanRequest = "GetServicePlan" - GetServicePlansRequest = "GetServicePlans" GetServicePlanVisibilityRequest = "GetServicePlanVisibility" + GetServicePlansRequest = "GetServicePlans" GetSpaceFeatureRequest = "GetSpaceFeatureRequest" - GetSpaceRelationshipIsolationSegmentRequest = "GetSpaceRelationshipIsolationSegment" - GetSpaceRunningSecurityGroupsRequest = "GetSpaceRunningSecurityGroups" - GetSpacesRequest = "GetSpaces" GetSpaceQuotaRequest = "GetSpaceQuota" GetSpaceQuotasRequest = "GetSpaceQuotas" + GetSpaceRelationshipIsolationSegmentRequest = "GetSpaceRelationshipIsolationSegment" + GetSpaceRunningSecurityGroupsRequest = "GetSpaceRunningSecurityGroups" GetSpaceStagingSecurityGroupsRequest = "GetSpaceStagingSecurityGroups" - GetSSHEnabled = "GetSSHEnabled" + GetSpacesRequest = "GetSpaces" GetStacksRequest = "GetStacks" GetTasksRequest = "GetTasks" GetTaskRequest = "GetTask" @@ -110,16 +110,16 @@ const ( MapRouteRequest = "MapRoute" PatchApplicationCurrentDropletRequest = "PatchApplicationCurrentDroplet" PatchApplicationEnvironmentVariablesRequest = "PatchApplicationEnvironmentVariables" - PatchApplicationRequest = "PatchApplication" PatchApplicationFeaturesRequest = "PatchApplicationFeatures" - PatchEnvironmentVariableGroupRequest = "PatchEnvironmentVariableGroup" + PatchApplicationRequest = "PatchApplication" PatchBuildpackRequest = "PatchBuildpack" PatchDestinationRequest = "PatchDestination" PatchDomainRequest = "PatchDomain" + PatchEnvironmentVariableGroupRequest = "PatchEnvironmentVariableGroup" PatchFeatureFlagRequest = "PatchFeatureFlag" + PatchOrganizationQuotaRequest = "PatchOrganizationQuota" PatchOrganizationRelationshipDefaultIsolationSegmentRequest = "PatchOrganizationRelationshipDefaultIsolationSegment" PatchOrganizationRequest = "PatchOrganization" - PatchOrganizationQuotaRequest = "PatchOrganizationQuota" PatchProcessRequest = "PatchProcess" PatchRouteRequest = "PatchRoute" PatchSecurityGroupRequest = "PatchSecurityGroup" @@ -127,10 +127,10 @@ const ( PatchServiceInstanceRequest = "PatchServiceInstance" PatchServiceOfferingRequest = "PatchServiceOfferingRequest" PatchServicePlanRequest = "PatchServicePlanRequest" - PatchSpaceRelationshipIsolationSegmentRequest = "PatchSpaceRelationshipIsolationSegment" - PatchSpaceRequest = "PatchSpace" PatchSpaceFeaturesRequest = "PatchSpaceFeatures" PatchSpaceQuotaRequest = "PatchSpaceQuota" + PatchSpaceRelationshipIsolationSegmentRequest = "PatchSpaceRelationshipIsolationSegment" + PatchSpaceRequest = "PatchSpace" PatchStackRequest = "PatchStack" PatchMoveRouteRequest = "PatchMoveRouteRequest" PostApplicationActionApplyManifest = "PostApplicationActionApplyM" @@ -151,18 +151,18 @@ const ( PostDropletRequest = "PostDroplet" PostIsolationSegmentRelationshipOrganizationsRequest = "PostIsolationSegmentRelationshipOrganizations" PostIsolationSegmentsRequest = "PostIsolationSegments" - PostOrganizationRequest = "PostOrganization" - PostOrganizationQuotaRequest = "PostOrganizationQuota" PostOrganizationQuotaApplyRequest = "PostOrganizationQuotaApply" - PostPackageRequest = "PostPackage" + PostOrganizationQuotaRequest = "PostOrganizationQuota" + PostOrganizationRequest = "PostOrganization" PostPackageBitsRequest = "PostPackageBits" + PostPackageRequest = "PostPackage" PostResourceMatchesRequest = "PostResourceMatches" PostRoleRequest = "PostRole" PostRouteRequest = "PostRoute" PostRouteBindingRequest = "PostRouteBinding" PostSecurityGroupRequest = "PostSecurityGroup" - PostSecurityGroupStagingSpaceRequest = "PostSecurityGroupStagingSpace" PostSecurityGroupRunningSpaceRequest = "PostSecurityGroupRunningSpace" + PostSecurityGroupStagingSpaceRequest = "PostSecurityGroupStagingSpace" PostServiceCredentialBindingRequest = "PostServiceCredentialBinding" PostServiceBrokerRequest = "PostServiceBroker" PostServiceInstanceRequest = "PostServiceInstance" @@ -170,9 +170,9 @@ const ( PostServicePlanVisibilityRequest = "PostServicePlanVisibility" PostSpaceActionApplyManifestRequest = "PostSpaceActionApplyManifest" PostSpaceDiffManifestRequest = "PostSpaceDiffManifest" - PostSpaceRequest = "PostSpace" - PostSpaceQuotaRequest = "PostSpaceQuota" PostSpaceQuotaRelationshipsRequest = "PostSpaceQuotaRelationships" + PostSpaceQuotaRequest = "PostSpaceQuota" + PostSpaceRequest = "PostSpace" PostUserRequest = "PostUser" PutTaskCancelRequest = "PutTaskCancel" SharePrivateDomainRequest = "SharePrivateDomainRequest" diff --git a/api/cloudcontroller/ccv3/revisions.go b/api/cloudcontroller/ccv3/revisions.go index 67b14a0fc53..d50fca2630b 100644 --- a/api/cloudcontroller/ccv3/revisions.go +++ b/api/cloudcontroller/ccv3/revisions.go @@ -35,3 +35,14 @@ func (client *Client) GetApplicationRevisionsDeployed(appGUID string) ([]resourc }) return revisions, warnings, err } + +func (client *Client) GetEnvironmentVariablesByURL(url string) (resources.EnvironmentVariables, Warnings, error) { + environmentVariables := make(resources.EnvironmentVariables) + + _, warnings, err := client.MakeRequest(RequestParams{ + URL: url, + ResponseBody: &environmentVariables, + }) + + return environmentVariables, warnings, err +} diff --git a/api/cloudcontroller/ccv3/revisions_test.go b/api/cloudcontroller/ccv3/revisions_test.go index fe3ac699ea2..afeb59fbb7e 100644 --- a/api/cloudcontroller/ccv3/revisions_test.go +++ b/api/cloudcontroller/ccv3/revisions_test.go @@ -8,6 +8,7 @@ import ( "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/ccv3fakes" "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/internal" "code.cloudfoundry.org/cli/resources" + "code.cloudfoundry.org/cli/types" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) @@ -133,4 +134,79 @@ var _ = Describe("Revisions", func() { }) }) }) + + Describe("GetEnvironmentVariablesByURL", func() { + var ( + warnings Warnings + executeErr error + environmentVariables resources.EnvironmentVariables + ) + + JustBeforeEach(func() { + environmentVariables, warnings, executeErr = client.GetEnvironmentVariablesByURL("url") + }) + + When("the cloud controller returns errors and warnings", func() { + BeforeEach(func() { + errors := []ccerror.V3Error{ + { + Code: 10008, + Detail: "The request is semantically invalid: command presence", + Title: "CF-UnprocessableEntity", + }, + { + Code: 10010, + Detail: "App not found", + Title: "CF-ResourceNotFound", + }, + } + + requester.MakeRequestReturns( + "url", + Warnings{"this is a warning"}, + ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, + ) + }) + + It("returns the error and all warnings", func() { + Expect(executeErr).To(MatchError(ccerror.MultiError{ + ResponseCode: http.StatusTeapot, + Errors: []ccerror.V3Error{ + { + Code: 10008, + Detail: "The request is semantically invalid: command presence", + Title: "CF-UnprocessableEntity", + }, + { + Code: 10010, + Detail: "App not found", + Title: "CF-ResourceNotFound", + }, + }, + })) + Expect(warnings).To(ConsistOf("this is a warning")) + }) + }) + + When("revision exist", func() { + BeforeEach(func() { + requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { + (*requestParams.ResponseBody.(*resources.EnvironmentVariables))["foo"] = *types.NewFilteredString("bar") + return "url", Warnings{"this is a warning"}, nil + }) + }) + + It("returns the environment variables and all warnings", func() { + Expect(requester.MakeRequestCallCount()).To(Equal(1)) + actualParams := requester.MakeRequestArgsForCall(0) + Expect(actualParams.URL).To(Equal("url")) + + Expect(executeErr).NotTo(HaveOccurred()) + Expect(warnings).To(ConsistOf("this is a warning")) + + Expect(len(environmentVariables)).To(Equal(1)) + Expect(environmentVariables["foo"].Value).To(Equal("bar")) + }) + }) + }) }) diff --git a/command/common/help_command.go b/command/common/help_command.go index 60aea1586d9..a7553bb8aa2 100644 --- a/command/common/help_command.go +++ b/command/common/help_command.go @@ -136,8 +136,6 @@ func (cmd HelpCommand) displayHelpFooter(cmdInfo map[string]sharedaction.Command cmd.UI.DisplayNonWrappingTable(sharedaction.AllCommandsIndent, cmd.globalOptionsTableData(), 25) cmd.UI.DisplayNewline() - - cmd.displayCommandGroups(internal.ExperimentalHelpCategoryList, cmdInfo, 34) } func (cmd HelpCommand) displayCommonCommands() { diff --git a/command/common/internal/help_all_display.go b/command/common/internal/help_all_display.go index b3a8ca198af..9de9243d3fc 100644 --- a/command/common/internal/help_all_display.go +++ b/command/common/internal/help_all_display.go @@ -17,7 +17,7 @@ var HelpCategoryList = []HelpCategory{ {"start", "stop", "restart", "stage-package", "restage", "restart-app-instance"}, {"run-task", "task", "tasks", "terminate-task"}, {"packages", "create-package"}, - {"revisions", "rollback"}, + {"revision", "revisions", "rollback"}, {"droplets", "set-droplet", "download-droplet"}, {"events", "logs"}, {"env", "set-env", "unset-env"}, @@ -168,12 +168,3 @@ var HelpCategoryList = []HelpCategory{ }, }, } - -var ExperimentalHelpCategoryList = []HelpCategory{ - { - CategoryName: "EXPERIMENTAL COMMANDS:", - CommandList: [][]string{ - {"revision"}, - }, - }, -} diff --git a/command/common/internal/help_all_display_test.go b/command/common/internal/help_all_display_test.go index bb93ffb9ce9..695b5690566 100644 --- a/command/common/internal/help_all_display_test.go +++ b/command/common/internal/help_all_display_test.go @@ -34,16 +34,6 @@ var _ = Describe("test help all display", func() { } } } - - for _, category := range internal.ExperimentalHelpCategoryList { - for _, row := range category.CommandList { - for _, command := range row { - if command != "" { - fromHelpAllDisplay = append(fromHelpAllDisplay, command) - } - } - } - } }) It("lists all commands from command list in at least one category", func() { diff --git a/command/v7/actor.go b/command/v7/actor.go index ff5303ea855..d75347909ed 100644 --- a/command/v7/actor.go +++ b/command/v7/actor.go @@ -108,6 +108,7 @@ type Actor interface { GetDomainLabels(domainName string) (map[string]types.NullString, v7action.Warnings, error) GetEffectiveIsolationSegmentBySpace(spaceGUID string, orgDefaultIsolationSegmentGUID string) (resources.IsolationSegment, v7action.Warnings, error) GetEnvironmentVariableGroup(group constant.EnvironmentVariableGroupName) (v7action.EnvironmentVariableGroup, v7action.Warnings, error) + GetEnvironmentVariableGroupByRevision(revision resources.Revision) (v7action.EnvironmentVariableGroup, bool, v7action.Warnings, error) GetEnvironmentVariablesByApplicationNameAndSpace(appName string, spaceGUID string) (v7action.EnvironmentVariableGroups, v7action.Warnings, error) GetFeatureFlagByName(featureFlagName string) (resources.FeatureFlag, v7action.Warnings, error) GetFeatureFlags() ([]resources.FeatureFlag, v7action.Warnings, error) diff --git a/command/v7/revision_command.go b/command/v7/revision_command.go index ebd429291b4..814eff0c5dc 100644 --- a/command/v7/revision_command.go +++ b/command/v7/revision_command.go @@ -1,8 +1,13 @@ package v7 import ( - "code.cloudfoundry.org/cli/command" + "fmt" + "strconv" + + "code.cloudfoundry.org/cli/actor/v7action" "code.cloudfoundry.org/cli/command/flag" + "code.cloudfoundry.org/cli/resources" + "code.cloudfoundry.org/cli/types" ) type RevisionCommand struct { @@ -14,7 +19,118 @@ type RevisionCommand struct { } func (cmd RevisionCommand) Execute(_ []string) error { - cmd.UI.DisplayWarning(command.ExperimentalWarning) + err := cmd.SharedActor.CheckTarget(true, true) + if err != nil { + return err + } + + user, err := cmd.Config.CurrentUser() + if err != nil { + return err + } + + appName := cmd.RequiredArgs.AppName + cmd.UI.DisplayTextWithFlavor("Showing revision {{.Version}} for app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{ + "AppName": appName, + "OrgName": cmd.Config.TargetedOrganization().Name, + "SpaceName": cmd.Config.TargetedSpace().Name, + "Username": user.Name, + "Version": cmd.Version.Value, + }) + cmd.UI.DisplayNewline() + + app, warnings, err := cmd.Actor.GetApplicationByNameAndSpace(appName, cmd.Config.TargetedSpace().GUID) + cmd.UI.DisplayWarnings(warnings) + if err != nil { + return err + } + + deployedRevisions, warnings, err := cmd.Actor.GetApplicationRevisionsDeployed(app.GUID) + cmd.UI.DisplayWarnings(warnings) + if err != nil { + return err + } + + revision, warnings, err := cmd.Actor.GetRevisionByApplicationAndVersion( + app.GUID, + cmd.Version.Value, + ) + cmd.UI.DisplayWarnings(warnings) + if err != nil { + return err + } + isDeployed := revisionDeployed(revision, deployedRevisions) + + cmd.displayBasicRevisionInfo(revision, isDeployed) cmd.UI.DisplayNewline() + + cmd.UI.DisplayHeader("labels:") + labels := revision.Metadata.Labels + cmd.UI.DisplayWarnings(warnings) + if err != nil { + return err + } + + if len(labels) > 0 { + cmd.displayMetaData(labels) + cmd.UI.DisplayNewline() + } + + cmd.UI.DisplayHeader("annotations:") + annotations := revision.Metadata.Annotations + cmd.UI.DisplayWarnings(warnings) + + if len(annotations) > 0 { + cmd.displayMetaData(annotations) + cmd.UI.DisplayNewline() + } + + cmd.UI.DisplayHeader("application environment variables:") + envVars, isPresent, warnings, _ := cmd.Actor.GetEnvironmentVariableGroupByRevision(revision) + cmd.UI.DisplayWarnings(warnings) + if isPresent { + cmd.displayEnvVarGroup(envVars) + cmd.UI.DisplayNewline() + } + return nil } + +func (cmd RevisionCommand) displayBasicRevisionInfo(revision resources.Revision, isDeployed bool) { + keyValueTable := [][]string{ + {"revision:", fmt.Sprintf("%d", cmd.Version.Value)}, + {"deployed:", strconv.FormatBool(isDeployed)}, + {"description:", revision.Description}, + {"deployable:", strconv.FormatBool(revision.Deployable)}, + {"revision GUID:", revision.GUID}, + {"droplet GUID:", revision.Droplet.GUID}, + {"created on:", revision.CreatedAt}, + } + cmd.UI.DisplayKeyValueTable("", keyValueTable, 3) +} + +func (cmd RevisionCommand) displayEnvVarGroup(envVarGroup v7action.EnvironmentVariableGroup) { + envVarTable := [][]string{} + for k, v := range envVarGroup { + envVarTable = append(envVarTable, []string{fmt.Sprintf("%s:", k), v.Value}) + } + cmd.UI.DisplayKeyValueTable("", envVarTable, 3) +} + +func (cmd RevisionCommand) displayMetaData(data map[string]types.NullString) { + tableData := [][]string{} + for k, v := range data { + tableData = append(tableData, []string{fmt.Sprintf("%s:", k), v.Value}) + } + cmd.UI.DisplayKeyValueTable("", tableData, 3) + +} + +func revisionDeployed(revision resources.Revision, deployedRevisions []resources.Revision) bool { + for _, deployedRevision := range deployedRevisions { + if revision.GUID == deployedRevision.GUID { + return true + } + } + return false +} diff --git a/command/v7/revision_command_test.go b/command/v7/revision_command_test.go index 2b31c71045f..659ec9e8ad6 100644 --- a/command/v7/revision_command_test.go +++ b/command/v7/revision_command_test.go @@ -1,9 +1,17 @@ package v7_test import ( + "errors" + + "code.cloudfoundry.org/cli/actor/actionerror" + "code.cloudfoundry.org/cli/actor/v7action" "code.cloudfoundry.org/cli/command/commandfakes" + "code.cloudfoundry.org/cli/command/flag" v7 "code.cloudfoundry.org/cli/command/v7" "code.cloudfoundry.org/cli/command/v7/v7fakes" + "code.cloudfoundry.org/cli/resources" + "code.cloudfoundry.org/cli/types" + "code.cloudfoundry.org/cli/util/configv3" "code.cloudfoundry.org/cli/util/ui" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -18,6 +26,7 @@ var _ = Describe("revision Command", func() { fakeSharedActor *commandfakes.FakeSharedActor fakeActor *v7fakes.FakeActor binaryName string + executeErr error appName string out *Buffer @@ -46,10 +55,218 @@ var _ = Describe("revision Command", func() { }) JustBeforeEach(func() { - Expect(cmd.Execute(nil)).To(Succeed()) + executeErr = cmd.Execute(nil) + }) + + When("checking target fails", func() { + BeforeEach(func() { + fakeSharedActor.CheckTargetReturns(actionerror.NotLoggedInError{BinaryName: binaryName}) + }) + + It("returns an error", func() { + Expect(executeErr).To(MatchError(actionerror.NotLoggedInError{BinaryName: binaryName})) + + Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1)) + checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0) + Expect(checkTargetedOrg).To(BeTrue()) + Expect(checkTargetedSpace).To(BeTrue()) + }) }) - It("displays the experimental warning", func() { - Expect(testUI.Err).To(Say("This command is in EXPERIMENTAL stage and may change without notice")) + When("the user is logged in, an org is targeted and a space is targeted", func() { + BeforeEach(func() { + fakeConfig.TargetedSpaceReturns(configv3.Space{Name: "some-space", GUID: "some-space-guid"}) + fakeConfig.TargetedOrganizationReturns(configv3.Organization{Name: "some-org"}) + }) + + When("getting the current user returns an error", func() { + BeforeEach(func() { + fakeConfig.CurrentUserReturns(configv3.User{}, errors.New("some-error")) + }) + + It("returns the error", func() { + Expect(executeErr).To(MatchError("some-error")) + }) + }) + + When("getting the current user succeeds", func() { + BeforeEach(func() { + fakeConfig.CurrentUserReturns(configv3.User{Name: "banana"}, nil) + }) + + When("when the requested app and revision exist", func() { + var revision resources.Revision + BeforeEach(func() { + fakeApp := resources.Application{ + GUID: "fake-guid", + Name: "some-app", + } + fakeActor.GetApplicationByNameAndSpaceReturns(fakeApp, nil, nil) + + revision = resources.Revision{ + Version: 3, + GUID: "A68F13F7-7E5E-4411-88E8-1FAC54F73F50", + Description: "On a different note", + CreatedAt: "2020-03-10T17:11:58Z", + Deployable: true, + Droplet: resources.Droplet{ + GUID: "droplet-guid", + }, + Links: resources.APILinks{ + "environment_variables": resources.APILink{ + HREF: "revision-environment-variables-link-3", + }, + }, + Metadata: &resources.Metadata{ + Labels: map[string]types.NullString{ + "label": types.NewNullString("foo3"), + }, + Annotations: map[string]types.NullString{ + "annotation": types.NewNullString("foo3"), + }, + }, + } + fakeActor.GetRevisionByApplicationAndVersionReturns(revision, nil, nil) + fakeActor.GetApplicationByNameAndSpaceReturns(resources.Application{GUID: "app-guid"}, nil, nil) + fakeActor.GetApplicationRevisionsDeployedReturns([]resources.Revision{revision}, nil, nil) + + environmentVariableGroup := v7action.EnvironmentVariableGroup{ + "foo": *types.NewFilteredString("bar3"), + } + fakeActor.GetEnvironmentVariableGroupByRevisionReturns( + environmentVariableGroup, + true, + nil, + nil, + ) + + cmd.Version = flag.Revision{NullInt: types.NullInt{Value: 3, IsSet: true}} + }) + + It("gets the app guid", func() { + Expect(fakeActor.GetApplicationByNameAndSpaceCallCount()).To(Equal(1)) + appName, spaceGUID := fakeActor.GetApplicationByNameAndSpaceArgsForCall(0) + Expect(appName).To(Equal("some-app")) + Expect(spaceGUID).To(Equal("some-space-guid")) + }) + + It("retrieves the requested revision for the app", func() { + Expect(fakeActor.GetRevisionByApplicationAndVersionCallCount()).To(Equal(1)) + appGUID, version := fakeActor.GetRevisionByApplicationAndVersionArgsForCall(0) + Expect(appGUID).To(Equal("app-guid")) + Expect(version).To(Equal(3)) + }) + + It("retrieves the deployed revisions", func() { + Expect(fakeActor.GetApplicationRevisionsDeployedCallCount()).To(Equal(1)) + Expect(fakeActor.GetApplicationRevisionsDeployedArgsForCall(0)).To(Equal("app-guid")) + }) + + It("retrieves the environment variables for the revision", func() { + Expect(fakeActor.GetEnvironmentVariableGroupByRevisionCallCount()).To(Equal(1)) + Expect(fakeActor.GetEnvironmentVariableGroupByRevisionArgsForCall(0)).To(Equal( + revision, + )) + }) + + It("displays the revision", func() { + Expect(executeErr).ToNot(HaveOccurred()) + + Expect(testUI.Out).To(Say(`Showing revision 3 for app some-app in org some-org / space some-space as banana...`)) + Expect(testUI.Out).To(Say(`revision: 3`)) + Expect(testUI.Out).To(Say(`deployed: true`)) + Expect(testUI.Out).To(Say(`description: On a different note`)) + Expect(testUI.Out).To(Say(`deployable: true`)) + Expect(testUI.Out).To(Say(`revision GUID: A68F13F7-7E5E-4411-88E8-1FAC54F73F50`)) + Expect(testUI.Out).To(Say(`droplet GUID: droplet-guid`)) + Expect(testUI.Out).To(Say(`created on: 2020-03-10T17:11:58Z`)) + + Expect(testUI.Out).To(Say(`labels:`)) + Expect(testUI.Out).To(Say(`label: foo3`)) + + Expect(testUI.Out).To(Say(`annotations:`)) + Expect(testUI.Out).To(Say(`annotation: foo3`)) + + Expect(testUI.Out).To(Say(`application environment variables:`)) + Expect(testUI.Out).To(Say(`foo: bar3`)) + + }) + + When("there are no environment_variables link and metadata provided", func() { + BeforeEach(func() { + revision = resources.Revision{ + Version: 3, + GUID: "A68F13F7-7E5E-4411-88E8-1FAC54F73F50", + Description: "No env var link", + CreatedAt: "2020-03-10T17:11:58Z", + Deployable: true, + Droplet: resources.Droplet{ + GUID: "droplet-guid", + }, + Links: resources.APILinks{}, + Metadata: &resources.Metadata{}, + } + fakeActor.GetRevisionByApplicationAndVersionReturns(revision, nil, nil) + fakeActor.GetApplicationRevisionsDeployedReturns([]resources.Revision{revision}, nil, nil) + fakeActor.GetEnvironmentVariableGroupByRevisionReturns(nil, false, v7action.Warnings{"warn-env-var"}, nil) + }) + + It("warns the user it will not display env vars ", func() { + Expect(executeErr).ToNot(HaveOccurred()) + Expect(testUI.Err).To(Say("warn-env-var")) + Expect(testUI.Out).To(Say("labels:")) + Expect(testUI.Out).To(Say("annotations:")) + Expect(testUI.Out).To(Say("application environment variables:")) + }) + }) + + When("revision is not deployed", func() { + BeforeEach(func() { + revisionDeployed := resources.Revision{ + Version: 12345, + GUID: "Fake-guid", + Description: "derployed and definitely not your revision", + CreatedAt: "2020-03-10T17:11:58Z", + Deployable: true, + Droplet: resources.Droplet{ + GUID: "droplet-guid", + }, + } + fakeActor.GetApplicationRevisionsDeployedReturns([]resources.Revision{revisionDeployed}, nil, nil) + }) + + It("displays deployed field correctly", func() { + Expect(testUI.Out).To(Say(`deployed: false`)) + }) + }) + + When("no revisions were deployed", func() { + BeforeEach(func() { + fakeActor.GetApplicationRevisionsDeployedReturns([]resources.Revision{}, nil, nil) + }) + + It("displays deployed field correctly", func() { + Expect(testUI.Out).To(Say(`deployed: false`)) + }) + }) + }) + + When("there are no revisions available", func() { + BeforeEach(func() { + revision := resources.Revision{ + Version: 120, + } + fakeActor.GetRevisionByApplicationAndVersionReturns( + revision, + nil, + errors.New("Revision 120 not found"), + ) + }) + + It("returns 'revision not found'", func() { + Expect(executeErr).To(MatchError("Revision 120 not found")) + }) + }) + }) }) }) diff --git a/command/v7/revisions_command.go b/command/v7/revisions_command.go index 2c309350b61..c9e6373fe22 100644 --- a/command/v7/revisions_command.go +++ b/command/v7/revisions_command.go @@ -21,7 +21,7 @@ type RevisionsCommand struct { usage interface{} `usage:"CF_NAME revisions APP_NAME"` BaseCommand - relatedCommands interface{} `related_commands:"rollback"` + relatedCommands interface{} `related_commands:"revision, rollback"` } func (cmd RevisionsCommand) Execute(_ []string) error { @@ -90,6 +90,11 @@ func (cmd RevisionsCommand) Execute(_ []string) error { return err } + if len(revisionsDeployed) > 1 { + cmd.UI.DisplayText("Info: this app is in the middle of a rolling deployment. More than one revision is deployed.") + cmd.UI.DisplayNewline() + } + table := [][]string{{ "revision", "description", diff --git a/command/v7/rollback_command.go b/command/v7/rollback_command.go index 29c7c2e7169..477b75bdeea 100644 --- a/command/v7/rollback_command.go +++ b/command/v7/rollback_command.go @@ -20,7 +20,7 @@ type RollbackCommand struct { MaxInFlight *int `long:"max-in-flight" description:"Defines the maximum number of instances that will be actively being rolled back."` Strategy flag.DeploymentStrategy `long:"strategy" description:"Deployment strategy can be canary or rolling. When not specified, it defaults to rolling."` Version flag.Revision `long:"version" required:"true" description:"Roll back to the specified revision"` - relatedCommands interface{} `related_commands:"revisions"` + relatedCommands interface{} `related_commands:"revision, revisions"` usage interface{} `usage:"CF_NAME rollback APP_NAME [--version VERSION] [-f]"` LogCacheClient sharedaction.LogCacheClient diff --git a/command/v7/v7fakes/fake_actor.go b/command/v7/v7fakes/fake_actor.go index 12df707c491..62df07b77aa 100644 --- a/command/v7/v7fakes/fake_actor.go +++ b/command/v7/v7fakes/fake_actor.go @@ -1328,6 +1328,23 @@ type FakeActor struct { result2 v7action.Warnings result3 error } + GetEnvironmentVariableGroupByRevisionStub func(resources.Revision) (v7action.EnvironmentVariableGroup, bool, v7action.Warnings, error) + getEnvironmentVariableGroupByRevisionMutex sync.RWMutex + getEnvironmentVariableGroupByRevisionArgsForCall []struct { + arg1 resources.Revision + } + getEnvironmentVariableGroupByRevisionReturns struct { + result1 v7action.EnvironmentVariableGroup + result2 bool + result3 v7action.Warnings + result4 error + } + getEnvironmentVariableGroupByRevisionReturnsOnCall map[int]struct { + result1 v7action.EnvironmentVariableGroup + result2 bool + result3 v7action.Warnings + result4 error + } GetEnvironmentVariablesByApplicationNameAndSpaceStub func(string, string) (v7action.EnvironmentVariableGroups, v7action.Warnings, error) getEnvironmentVariablesByApplicationNameAndSpaceMutex sync.RWMutex getEnvironmentVariablesByApplicationNameAndSpaceArgsForCall []struct { @@ -9401,6 +9418,76 @@ func (fake *FakeActor) GetEnvironmentVariableGroupReturnsOnCall(i int, result1 v }{result1, result2, result3} } +func (fake *FakeActor) GetEnvironmentVariableGroupByRevision(arg1 resources.Revision) (v7action.EnvironmentVariableGroup, bool, v7action.Warnings, error) { + fake.getEnvironmentVariableGroupByRevisionMutex.Lock() + ret, specificReturn := fake.getEnvironmentVariableGroupByRevisionReturnsOnCall[len(fake.getEnvironmentVariableGroupByRevisionArgsForCall)] + fake.getEnvironmentVariableGroupByRevisionArgsForCall = append(fake.getEnvironmentVariableGroupByRevisionArgsForCall, struct { + arg1 resources.Revision + }{arg1}) + stub := fake.GetEnvironmentVariableGroupByRevisionStub + fakeReturns := fake.getEnvironmentVariableGroupByRevisionReturns + fake.recordInvocation("GetEnvironmentVariableGroupByRevision", []interface{}{arg1}) + fake.getEnvironmentVariableGroupByRevisionMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2, ret.result3, ret.result4 + } + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3, fakeReturns.result4 +} + +func (fake *FakeActor) GetEnvironmentVariableGroupByRevisionCallCount() int { + fake.getEnvironmentVariableGroupByRevisionMutex.RLock() + defer fake.getEnvironmentVariableGroupByRevisionMutex.RUnlock() + return len(fake.getEnvironmentVariableGroupByRevisionArgsForCall) +} + +func (fake *FakeActor) GetEnvironmentVariableGroupByRevisionCalls(stub func(resources.Revision) (v7action.EnvironmentVariableGroup, bool, v7action.Warnings, error)) { + fake.getEnvironmentVariableGroupByRevisionMutex.Lock() + defer fake.getEnvironmentVariableGroupByRevisionMutex.Unlock() + fake.GetEnvironmentVariableGroupByRevisionStub = stub +} + +func (fake *FakeActor) GetEnvironmentVariableGroupByRevisionArgsForCall(i int) resources.Revision { + fake.getEnvironmentVariableGroupByRevisionMutex.RLock() + defer fake.getEnvironmentVariableGroupByRevisionMutex.RUnlock() + argsForCall := fake.getEnvironmentVariableGroupByRevisionArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeActor) GetEnvironmentVariableGroupByRevisionReturns(result1 v7action.EnvironmentVariableGroup, result2 bool, result3 v7action.Warnings, result4 error) { + fake.getEnvironmentVariableGroupByRevisionMutex.Lock() + defer fake.getEnvironmentVariableGroupByRevisionMutex.Unlock() + fake.GetEnvironmentVariableGroupByRevisionStub = nil + fake.getEnvironmentVariableGroupByRevisionReturns = struct { + result1 v7action.EnvironmentVariableGroup + result2 bool + result3 v7action.Warnings + result4 error + }{result1, result2, result3, result4} +} + +func (fake *FakeActor) GetEnvironmentVariableGroupByRevisionReturnsOnCall(i int, result1 v7action.EnvironmentVariableGroup, result2 bool, result3 v7action.Warnings, result4 error) { + fake.getEnvironmentVariableGroupByRevisionMutex.Lock() + defer fake.getEnvironmentVariableGroupByRevisionMutex.Unlock() + fake.GetEnvironmentVariableGroupByRevisionStub = nil + if fake.getEnvironmentVariableGroupByRevisionReturnsOnCall == nil { + fake.getEnvironmentVariableGroupByRevisionReturnsOnCall = make(map[int]struct { + result1 v7action.EnvironmentVariableGroup + result2 bool + result3 v7action.Warnings + result4 error + }) + } + fake.getEnvironmentVariableGroupByRevisionReturnsOnCall[i] = struct { + result1 v7action.EnvironmentVariableGroup + result2 bool + result3 v7action.Warnings + result4 error + }{result1, result2, result3, result4} +} + func (fake *FakeActor) GetEnvironmentVariablesByApplicationNameAndSpace(arg1 string, arg2 string) (v7action.EnvironmentVariableGroups, v7action.Warnings, error) { fake.getEnvironmentVariablesByApplicationNameAndSpaceMutex.Lock() ret, specificReturn := fake.getEnvironmentVariablesByApplicationNameAndSpaceReturnsOnCall[len(fake.getEnvironmentVariablesByApplicationNameAndSpaceArgsForCall)] @@ -19650,6 +19737,8 @@ func (fake *FakeActor) Invocations() map[string][][]interface{} { defer fake.getEffectiveIsolationSegmentBySpaceMutex.RUnlock() fake.getEnvironmentVariableGroupMutex.RLock() defer fake.getEnvironmentVariableGroupMutex.RUnlock() + fake.getEnvironmentVariableGroupByRevisionMutex.RLock() + defer fake.getEnvironmentVariableGroupByRevisionMutex.RUnlock() fake.getEnvironmentVariablesByApplicationNameAndSpaceMutex.RLock() defer fake.getEnvironmentVariablesByApplicationNameAndSpaceMutex.RUnlock() fake.getFeatureFlagByNameMutex.RLock() diff --git a/integration/v7/isolated/revision_command_test.go b/integration/v7/isolated/revision_command_test.go index 47cf099fc4f..459fa3d488a 100644 --- a/integration/v7/isolated/revision_command_test.go +++ b/integration/v7/isolated/revision_command_test.go @@ -1,6 +1,12 @@ package isolated import ( + "bytes" + "encoding/json" + "fmt" + "os/exec" + "strings" + . "code.cloudfoundry.org/cli/cf/util/testhelpers/matchers" "code.cloudfoundry.org/cli/integration/helpers" . "github.com/onsi/ginkgo/v2" @@ -10,12 +16,26 @@ import ( ) var _ = Describe("revision command", func() { + var ( + orgName string + spaceName string + appName string + username string + ) + + BeforeEach(func() { + username, _ = helpers.GetCredentials() + orgName = helpers.NewOrgName() + spaceName = helpers.NewSpaceName() + appName = helpers.PrefixedRandomName("app") + }) + Describe("help", func() { When("--help flag is set", func() { It("appears in cf help -a", func() { session := helpers.CF("help", "-a") Eventually(session).Should(Exit(0)) - Expect(session).To(HaveCommandInCategoryWithDescription("revision", "EXPERIMENTAL COMMANDS", "Show details for a specific app revision")) + Expect(session).To(HaveCommandInCategoryWithDescription("revision", "APPS", "Show details for a specific app revision")) }) It("Displays revision command usage to output", func() { @@ -34,4 +54,109 @@ var _ = Describe("revision command", func() { }) }) }) + + When("targetting and org and space", func() { + BeforeEach(func() { + helpers.SetupCF(orgName, spaceName) + }) + + AfterEach(func() { + helpers.QuickDeleteOrg(orgName) + }) + + When("the requested revision version does not exist", func() { + BeforeEach(func() { + helpers.WithHelloWorldApp(func(appDir string) { + Eventually(helpers.CF("create-app", appName)).Should(Exit(0)) + Eventually(helpers.CF("set-env", appName, "foo", "bar1")).Should(Exit(0)) + Eventually(helpers.CF("push", appName, "-p", appDir)).Should(Exit(0)) + }) + }) + It("displays revision not found", func() { + session := helpers.CF("revision", appName, "--version", "125") + Eventually(session).Should(Exit(1)) + + Expect(session).Should(Say( + fmt.Sprintf("Showing revision 125 for app %s in org %s / space %s as %s...", appName, orgName, spaceName, username), + )) + Expect(session.Err).Should(Say("Revision '125' not found")) + }) + }) + + When("the requested app and revision both exist", func() { + BeforeEach(func() { + helpers.WithHelloWorldApp(func(appDir string) { + Eventually(helpers.CF("create-app", appName)).Should(Exit(0)) + Eventually(helpers.CF("set-env", appName, "foo", "bar1")).Should(Exit(0)) + Eventually(helpers.CF("push", appName, "-p", appDir)).Should(Exit(0)) + Eventually(helpers.CF("push", appName, "-p", appDir)).Should(Exit(0)) + }) + }) + + It("shows details about the revision", func() { + cmd := exec.Command("bash", "-c", "cf revision "+appName+" --version 1 | grep \"revision GUID\" | sed -e 's/.*:\\s*//' -e 's/^[ \\t]*//'") + var stdout bytes.Buffer + cmd.Stdout = &stdout + cmd.Run() + revisionGUID := strings.TrimSpace(stdout.String()) + data := map[string]interface{}{ + "metadata": map[string]interface{}{ + "labels": map[string]string{ + "label": "foo3", + }, + "annotations": map[string]string{ + "annotation": "foo3", + }, + }, + } + metadata, err := json.Marshal(data) + Expect(err).NotTo(HaveOccurred()) + + url := "/v3/revisions/" + string(revisionGUID) + Eventually(helpers.CF("curl", "-X", "PATCH", url, "-d", string(metadata))).Should(Exit(0)) + + session := helpers.CF("revision", appName, "--version", "1") + Eventually(session).Should(Exit(0)) + + Expect(session).Should(Say( + fmt.Sprintf("Showing revision 1 for app %s in org %s / space %s as %s...", appName, orgName, spaceName, username), + )) + Expect(session).Should(Say(`revision: 1`)) + Expect(session).Should(Say(`deployed: false`)) + Expect(session).Should(Say(`description: Initial revision`)) + Expect(session).Should(Say(`deployable: true`)) + Expect(session).Should(Say(`revision GUID: \S+\n`)) + Expect(session).Should(Say(`droplet GUID: \S+\n`)) + Expect(session).Should(Say(`created on: \S+\n`)) + + Expect(session).Should(Say(`labels:`)) + Expect(session).Should(Say(`label: foo3`)) + + Expect(session).Should(Say(`annotations:`)) + Expect(session).Should(Say(`annotation: foo3`)) + + Expect(session).Should(Say(`application environment variables:`)) + Expect(session).Should(Say(`foo: bar1`)) + + session = helpers.CF("revision", appName, "--version", "2") + Eventually(session).Should(Exit(0)) + Expect(session).Should(Say( + fmt.Sprintf("Showing revision 2 for app %s in org %s / space %s as %s...", appName, orgName, spaceName, username), + )) + Expect(session).Should(Say(`revision: 2`)) + Expect(session).Should(Say(`deployed: true`)) + Expect(session).Should(Say(`description: New droplet deployed`)) + Expect(session).Should(Say(`deployable: true`)) + Expect(session).Should(Say(`revision GUID: \S+\n`)) + Expect(session).Should(Say(`droplet GUID: \S+\n`)) + Expect(session).Should(Say(`created on: \S+\n`)) + + Expect(session).Should(Say(`labels:`)) + Expect(session).Should(Say(`annotations:`)) + + Expect(session).Should(Say(`application environment variables:`)) + Expect(session).Should(Say(`foo: bar1`)) + }) + }) + }) }) diff --git a/integration/v7/isolated/revisions_command_test.go b/integration/v7/isolated/revisions_command_test.go index 952654b1b7a..bb718f0177c 100644 --- a/integration/v7/isolated/revisions_command_test.go +++ b/integration/v7/isolated/revisions_command_test.go @@ -43,7 +43,7 @@ var _ = Describe("revisions command", func() { Eventually(session).Should(Say("USAGE:")) Eventually(session).Should(Say("cf revisions APP_NAME")) Eventually(session).Should(Say("SEE ALSO:")) - Eventually(session).Should(Say("rollback")) + Eventually(session).Should(Say("revision, rollback")) Eventually(session).Should(Exit(0)) }) }) diff --git a/integration/v7/isolated/rollback_command_test.go b/integration/v7/isolated/rollback_command_test.go index e8c1b6c9431..e2bbc7b4f79 100644 --- a/integration/v7/isolated/rollback_command_test.go +++ b/integration/v7/isolated/rollback_command_test.go @@ -39,7 +39,7 @@ var _ = Describe("rollback command", func() { Expect(session).To(Say(`--strategy\s+Deployment strategy can be canary or rolling. When not specified, it defaults to rolling.`)) Expect(session).To(Say(`--version\s+Roll back to the specified revision`)) Expect(session).To(Say("SEE ALSO:")) - Expect(session).To(Say("revisions")) + Expect(session).To(Say("revision, revisions")) }) }) }) diff --git a/resources/metadata_resource.go b/resources/metadata_resource.go index 414fe1427de..afa1c180323 100644 --- a/resources/metadata_resource.go +++ b/resources/metadata_resource.go @@ -3,7 +3,8 @@ package resources import "code.cloudfoundry.org/cli/types" type Metadata struct { - Labels map[string]types.NullString `json:"labels,omitempty"` + Annotations map[string]types.NullString `json:"annotations,omitempty"` + Labels map[string]types.NullString `json:"labels,omitempty"` } type ResourceMetadata struct { diff --git a/resources/revision_resource.go b/resources/revision_resource.go index 39d577a26ef..e187c951f01 100644 --- a/resources/revision_resource.go +++ b/resources/revision_resource.go @@ -1,11 +1,13 @@ package resources type Revision struct { - GUID string `json:"guid"` - Version int `json:"version"` - Deployable bool `json:"deployable"` - Description string `json:"description"` - Droplet Droplet `json:"droplet"` - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` + GUID string `json:"guid"` + Version int `json:"version"` + Deployable bool `json:"deployable"` + Description string `json:"description"` + Droplet Droplet `json:"droplet"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Links APILinks `json:"links"` + Metadata *Metadata `json:"metadata,omitempty"` } From 3e774575e8fe87257799b22378d9b00573d02d0f Mon Sep 17 00:00:00 2001 From: Shwetha Gururaj Date: Wed, 18 Dec 2024 13:24:56 -0500 Subject: [PATCH 2/4] Fix lint errors --- integration/v7/isolated/revision_command_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/integration/v7/isolated/revision_command_test.go b/integration/v7/isolated/revision_command_test.go index 459fa3d488a..87b5c19dc4b 100644 --- a/integration/v7/isolated/revision_command_test.go +++ b/integration/v7/isolated/revision_command_test.go @@ -97,7 +97,10 @@ var _ = Describe("revision command", func() { cmd := exec.Command("bash", "-c", "cf revision "+appName+" --version 1 | grep \"revision GUID\" | sed -e 's/.*:\\s*//' -e 's/^[ \\t]*//'") var stdout bytes.Buffer cmd.Stdout = &stdout - cmd.Run() + err := cmd.Run() + if err != nil { + return + } revisionGUID := strings.TrimSpace(stdout.String()) data := map[string]interface{}{ "metadata": map[string]interface{}{ From 9bc60906ff0a67f9ae40305ee915738a885cbea6 Mon Sep 17 00:00:00 2001 From: Shwetha Gururaj Date: Thu, 19 Dec 2024 13:14:06 -0500 Subject: [PATCH 3/4] rebase --- actor/v7action/cloud_controller_client.go | 26 +-- .../fake_cloud_controller_client.go | 214 ------------------ .../ccv3/internal/api_routes.go | 50 ++-- 3 files changed, 37 insertions(+), 253 deletions(-) diff --git a/actor/v7action/cloud_controller_client.go b/actor/v7action/cloud_controller_client.go index 787a34f0d3c..e8793ed0ea4 100644 --- a/actor/v7action/cloud_controller_client.go +++ b/actor/v7action/cloud_controller_client.go @@ -14,8 +14,6 @@ import ( // CloudControllerClient is the interface to the cloud controller V3 API. type CloudControllerClient interface { - AppSSHEndpoint() string - AppSSHHostKeyFingerprint() string ApplyOrganizationQuota(quotaGUID string, orgGUID string) (resources.RelationshipList, ccv3.Warnings, error) ApplySpaceQuota(quotaGUID string, spaceGUID string) (resources.RelationshipList, ccv3.Warnings, error) CheckRoute(domainGUID string, hostname string, path string, port int) (bool, ccv3.Warnings, error) @@ -37,10 +35,10 @@ type CloudControllerClient interface { CreateRole(role resources.Role) (resources.Role, ccv3.Warnings, error) CreateRoute(route resources.Route) (resources.Route, ccv3.Warnings, error) CreateRouteBinding(binding resources.RouteBinding) (ccv3.JobURL, ccv3.Warnings, error) + CreateServiceBroker(serviceBroker resources.ServiceBroker) (ccv3.JobURL, ccv3.Warnings, error) CreateServiceCredentialBinding(binding resources.ServiceCredentialBinding) (ccv3.JobURL, ccv3.Warnings, error) CreateServiceInstance(serviceInstance resources.ServiceInstance) (ccv3.JobURL, ccv3.Warnings, error) CreateSecurityGroup(securityGroup resources.SecurityGroup) (resources.SecurityGroup, ccv3.Warnings, error) - CreateServiceBroker(serviceBroker resources.ServiceBroker) (ccv3.JobURL, ccv3.Warnings, error) CreateSpace(space resources.Space) (resources.Space, ccv3.Warnings, error) CreateSpaceQuota(spaceQuota resources.SpaceQuota) (resources.SpaceQuota, ccv3.Warnings, error) CreateUser(userGUID string) (resources.User, ccv3.Warnings, error) @@ -60,12 +58,11 @@ type CloudControllerClient interface { DeleteServiceCredentialBinding(guid string) (ccv3.JobURL, ccv3.Warnings, error) DeleteServiceBroker(serviceBrokerGUID string) (ccv3.JobURL, ccv3.Warnings, error) DeleteServiceInstance(serviceInstanceGUID string, query ...ccv3.Query) (ccv3.JobURL, ccv3.Warnings, error) - DeleteSpace(guid string) (ccv3.JobURL, ccv3.Warnings, error) DeleteSpaceQuota(spaceQuotaGUID string) (ccv3.JobURL, ccv3.Warnings, error) + DeleteSpace(guid string) (ccv3.JobURL, ccv3.Warnings, error) DeleteUser(userGUID string) (ccv3.JobURL, ccv3.Warnings, error) DownloadDroplet(dropletGUID string) ([]byte, ccv3.Warnings, error) EntitleIsolationSegmentToOrganizations(isoGUID string, orgGUIDs []string) (resources.RelationshipList, ccv3.Warnings, error) - GetAppFeature(appGUID string, featureName string) (resources.ApplicationFeature, ccv3.Warnings, error) GetApplicationByNameAndSpace(appName string, spaceGUID string) (resources.Application, ccv3.Warnings, error) GetApplicationDropletCurrent(appGUID string) (resources.Droplet, ccv3.Warnings, error) GetApplicationEnvironment(appGUID string) (ccv3.Environment, ccv3.Warnings, error) @@ -103,18 +100,17 @@ type CloudControllerClient interface { GetOrganizationQuotas(query ...ccv3.Query) ([]resources.OrganizationQuota, ccv3.Warnings, error) GetOrganizations(query ...ccv3.Query) ([]resources.Organization, ccv3.Warnings, error) GetPackage(guid string) (resources.Package, ccv3.Warnings, error) - GetPackageDroplets(packageGUID string, query ...ccv3.Query) ([]resources.Droplet, ccv3.Warnings, error) GetPackages(query ...ccv3.Query) ([]resources.Package, ccv3.Warnings, error) + GetPackageDroplets(packageGUID string, query ...ccv3.Query) ([]resources.Droplet, ccv3.Warnings, error) GetProcess(processGUID string) (resources.Process, ccv3.Warnings, error) + GetProcesses(query ...ccv3.Query) ([]resources.Process, ccv3.Warnings, error) GetProcessInstances(processGUID string) ([]ccv3.ProcessInstance, ccv3.Warnings, error) GetProcessSidecars(processGUID string) ([]resources.Sidecar, ccv3.Warnings, error) - GetProcesses(query ...ccv3.Query) ([]resources.Process, ccv3.Warnings, error) GetRoles(query ...ccv3.Query) ([]resources.Role, ccv3.IncludedResources, ccv3.Warnings, error) GetRouteBindings(query ...ccv3.Query) ([]resources.RouteBinding, ccv3.IncludedResources, ccv3.Warnings, error) GetRouteDestinations(routeGUID string) ([]resources.RouteDestination, ccv3.Warnings, error) GetRoutes(query ...ccv3.Query) ([]resources.Route, ccv3.Warnings, error) GetRunningSecurityGroups(spaceGUID string, queries ...ccv3.Query) ([]resources.SecurityGroup, ccv3.Warnings, error) - GetSSHEnabled(appGUID string) (ccv3.SSHEnabled, ccv3.Warnings, error) GetSecurityGroups(query ...ccv3.Query) ([]resources.SecurityGroup, ccv3.Warnings, error) GetServiceBrokers(query ...ccv3.Query) ([]resources.ServiceBroker, ccv3.Warnings, error) GetServiceCredentialBindings(query ...ccv3.Query) ([]resources.ServiceCredentialBinding, ccv3.Warnings, error) @@ -125,9 +121,9 @@ type CloudControllerClient interface { GetServiceInstanceUsageSummary(serviceInstanceGUID string) ([]resources.ServiceInstanceUsageSummary, ccv3.Warnings, error) GetServiceInstances(query ...ccv3.Query) ([]resources.ServiceInstance, ccv3.IncludedResources, ccv3.Warnings, error) GetServiceOfferingByGUID(guid string) (resources.ServiceOffering, ccv3.Warnings, error) + GetServiceOfferings(query ...ccv3.Query) ([]resources.ServiceOffering, ccv3.Warnings, error) GetServiceOfferingByNameAndBroker(serviceOfferingName, serviceBrokerName string) (resources.ServiceOffering, ccv3.Warnings, error) GetServicePlanByGUID(guid string) (resources.ServicePlan, ccv3.Warnings, error) - GetServiceOfferings(query ...ccv3.Query) ([]resources.ServiceOffering, ccv3.Warnings, error) GetServicePlans(query ...ccv3.Query) ([]resources.ServicePlan, ccv3.Warnings, error) GetServicePlansWithOfferings(query ...ccv3.Query) ([]ccv3.ServiceOfferingWithPlans, ccv3.Warnings, error) GetServicePlansWithSpaceAndOrganization(query ...ccv3.Query) ([]ccv3.ServicePlanWithSpaceAndOrganization, ccv3.Warnings, error) @@ -135,8 +131,10 @@ type CloudControllerClient interface { GetSpaceIsolationSegment(spaceGUID string) (resources.Relationship, ccv3.Warnings, error) GetSpaceManifestDiff(spaceGUID string, rawManifest []byte) (resources.ManifestDiff, ccv3.Warnings, error) GetSpaceQuota(spaceQuotaGUID string) (resources.SpaceQuota, ccv3.Warnings, error) - GetSpaceQuotas(query ...ccv3.Query) ([]resources.SpaceQuota, ccv3.Warnings, error) GetSpaces(query ...ccv3.Query) ([]resources.Space, ccv3.IncludedResources, ccv3.Warnings, error) + GetSpaceQuotas(query ...ccv3.Query) ([]resources.SpaceQuota, ccv3.Warnings, error) + GetSSHEnabled(appGUID string) (ccv3.SSHEnabled, ccv3.Warnings, error) + GetAppFeature(appGUID string, featureName string) (resources.ApplicationFeature, ccv3.Warnings, error) GetStacks(query ...ccv3.Query) ([]resources.Stack, ccv3.Warnings, error) GetStagingSecurityGroups(spaceGUID string, queries ...ccv3.Query) ([]resources.SecurityGroup, ccv3.Warnings, error) GetTask(guid string) (resources.Task, ccv3.Warnings, error) @@ -160,7 +158,6 @@ type CloudControllerClient interface { UnbindSecurityGroupStagingSpace(securityGroupGUID string, spaceGUID string) (ccv3.Warnings, error) UnmapRoute(routeGUID string, destinationGUID string) (ccv3.Warnings, error) UnshareRoute(routeGUID string, spaceGUID string) (ccv3.Warnings, error) - UnsetSpaceQuota(spaceQuotaGUID, spaceGUID string) (ccv3.Warnings, error) UnsharePrivateDomainFromOrg(domainGUID string, sharedOrgGUID string) (ccv3.Warnings, error) UnshareServiceInstanceFromSpace(serviceInstanceGUID string, sharedToSpaceGUID string) (ccv3.Warnings, error) UpdateAppFeature(appGUID string, enabled bool, featureName string) (ccv3.Warnings, error) @@ -180,16 +177,17 @@ type CloudControllerClient interface { UpdateOrganizationQuota(orgQuota resources.OrganizationQuota) (resources.OrganizationQuota, ccv3.Warnings, error) UpdateProcess(process resources.Process) (resources.Process, ccv3.Warnings, error) UpdateResourceMetadata(resource string, resourceGUID string, metadata resources.Metadata) (ccv3.JobURL, ccv3.Warnings, error) - UpdateSecurityGroup(securityGroup resources.SecurityGroup) (resources.SecurityGroup, ccv3.Warnings, error) UpdateSecurityGroupRunningSpace(securityGroupGUID string, spaceGUIDs []string) (ccv3.Warnings, error) UpdateSecurityGroupStagingSpace(securityGroupGUID string, spaceGUIDs []string) (ccv3.Warnings, error) - UpdateServiceBroker(serviceBrokerGUID string, serviceBroker resources.ServiceBroker) (ccv3.JobURL, ccv3.Warnings, error) + UpdateSecurityGroup(securityGroup resources.SecurityGroup) (resources.SecurityGroup, ccv3.Warnings, error) UpdateServiceInstance(serviceInstanceGUID string, serviceInstanceUpdates resources.ServiceInstance) (ccv3.JobURL, ccv3.Warnings, error) UpdateSpace(space resources.Space) (resources.Space, ccv3.Warnings, error) UpdateSpaceApplyManifest(spaceGUID string, rawManifest []byte) (ccv3.JobURL, ccv3.Warnings, error) UpdateSpaceFeature(spaceGUID string, enabled bool, featureName string) (ccv3.Warnings, error) UpdateSpaceIsolationSegmentRelationship(spaceGUID string, isolationSegmentGUID string) (resources.Relationship, ccv3.Warnings, error) UpdateSpaceQuota(spaceQuota resources.SpaceQuota) (resources.SpaceQuota, ccv3.Warnings, error) + UnsetSpaceQuota(spaceQuotaGUID, spaceGUID string) (ccv3.Warnings, error) + UpdateServiceBroker(serviceBrokerGUID string, serviceBroker resources.ServiceBroker) (ccv3.JobURL, ccv3.Warnings, error) UpdateTaskCancel(taskGUID string) (resources.Task, ccv3.Warnings, error) UploadBitsPackage(pkg resources.Package, matchedResources []ccv3.Resource, newResources io.Reader, newResourcesLength int64) (resources.Package, ccv3.Warnings, error) UploadBuildpack(buildpackGUID string, buildpackPath string, buildpack io.Reader, buildpackLength int64) (ccv3.JobURL, ccv3.Warnings, error) @@ -201,9 +199,9 @@ type CloudControllerClient interface { } type servicePlanVisibilityClient interface { - DeleteServicePlanVisibility(servicePlanGUID, organizationGUID string) (ccv3.Warnings, error) GetServicePlanVisibility(servicePlanGUID string) (resources.ServicePlanVisibility, ccv3.Warnings, error) UpdateServicePlanVisibility(servicePlanGUID string, visibility resources.ServicePlanVisibility) (resources.ServicePlanVisibility, ccv3.Warnings, error) + DeleteServicePlanVisibility(servicePlanGUID, organizationGUID string) (ccv3.Warnings, error) } // TODO: Split this enormous interface diff --git a/actor/v7action/v7actionfakes/fake_cloud_controller_client.go b/actor/v7action/v7actionfakes/fake_cloud_controller_client.go index 666b5a40695..f9035d65ab1 100644 --- a/actor/v7action/v7actionfakes/fake_cloud_controller_client.go +++ b/actor/v7action/v7actionfakes/fake_cloud_controller_client.go @@ -14,26 +14,6 @@ import ( ) type FakeCloudControllerClient struct { - AppSSHEndpointStub func() string - appSSHEndpointMutex sync.RWMutex - appSSHEndpointArgsForCall []struct { - } - appSSHEndpointReturns struct { - result1 string - } - appSSHEndpointReturnsOnCall map[int]struct { - result1 string - } - AppSSHHostKeyFingerprintStub func() string - appSSHHostKeyFingerprintMutex sync.RWMutex - appSSHHostKeyFingerprintArgsForCall []struct { - } - appSSHHostKeyFingerprintReturns struct { - result1 string - } - appSSHHostKeyFingerprintReturnsOnCall map[int]struct { - result1 string - } ApplyOrganizationQuotaStub func(string, string) (resources.RelationshipList, ccv3.Warnings, error) applyOrganizationQuotaMutex sync.RWMutex applyOrganizationQuotaArgsForCall []struct { @@ -1452,21 +1432,6 @@ type FakeCloudControllerClient struct { result2 ccv3.Warnings result3 error } - GetRevisionStub func(string) (resources.Revision, ccv3.Warnings, error) - getRevisionMutex sync.RWMutex - getRevisionArgsForCall []struct { - arg1 string - } - getRevisionReturns struct { - result1 resources.Revision - result2 ccv3.Warnings - result3 error - } - getRevisionReturnsOnCall map[int]struct { - result1 resources.Revision - result2 ccv3.Warnings - result3 error - } GetRolesStub func(...ccv3.Query) ([]resources.Role, ccv3.IncludedResources, ccv3.Warnings, error) getRolesMutex sync.RWMutex getRolesArgsForCall []struct { @@ -2817,112 +2782,6 @@ type FakeCloudControllerClient struct { invocationsMutex sync.RWMutex } -func (fake *FakeCloudControllerClient) AppSSHEndpoint() string { - fake.appSSHEndpointMutex.Lock() - ret, specificReturn := fake.appSSHEndpointReturnsOnCall[len(fake.appSSHEndpointArgsForCall)] - fake.appSSHEndpointArgsForCall = append(fake.appSSHEndpointArgsForCall, struct { - }{}) - stub := fake.AppSSHEndpointStub - fakeReturns := fake.appSSHEndpointReturns - fake.recordInvocation("AppSSHEndpoint", []interface{}{}) - fake.appSSHEndpointMutex.Unlock() - if stub != nil { - return stub() - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeCloudControllerClient) AppSSHEndpointCallCount() int { - fake.appSSHEndpointMutex.RLock() - defer fake.appSSHEndpointMutex.RUnlock() - return len(fake.appSSHEndpointArgsForCall) -} - -func (fake *FakeCloudControllerClient) AppSSHEndpointCalls(stub func() string) { - fake.appSSHEndpointMutex.Lock() - defer fake.appSSHEndpointMutex.Unlock() - fake.AppSSHEndpointStub = stub -} - -func (fake *FakeCloudControllerClient) AppSSHEndpointReturns(result1 string) { - fake.appSSHEndpointMutex.Lock() - defer fake.appSSHEndpointMutex.Unlock() - fake.AppSSHEndpointStub = nil - fake.appSSHEndpointReturns = struct { - result1 string - }{result1} -} - -func (fake *FakeCloudControllerClient) AppSSHEndpointReturnsOnCall(i int, result1 string) { - fake.appSSHEndpointMutex.Lock() - defer fake.appSSHEndpointMutex.Unlock() - fake.AppSSHEndpointStub = nil - if fake.appSSHEndpointReturnsOnCall == nil { - fake.appSSHEndpointReturnsOnCall = make(map[int]struct { - result1 string - }) - } - fake.appSSHEndpointReturnsOnCall[i] = struct { - result1 string - }{result1} -} - -func (fake *FakeCloudControllerClient) AppSSHHostKeyFingerprint() string { - fake.appSSHHostKeyFingerprintMutex.Lock() - ret, specificReturn := fake.appSSHHostKeyFingerprintReturnsOnCall[len(fake.appSSHHostKeyFingerprintArgsForCall)] - fake.appSSHHostKeyFingerprintArgsForCall = append(fake.appSSHHostKeyFingerprintArgsForCall, struct { - }{}) - stub := fake.AppSSHHostKeyFingerprintStub - fakeReturns := fake.appSSHHostKeyFingerprintReturns - fake.recordInvocation("AppSSHHostKeyFingerprint", []interface{}{}) - fake.appSSHHostKeyFingerprintMutex.Unlock() - if stub != nil { - return stub() - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeCloudControllerClient) AppSSHHostKeyFingerprintCallCount() int { - fake.appSSHHostKeyFingerprintMutex.RLock() - defer fake.appSSHHostKeyFingerprintMutex.RUnlock() - return len(fake.appSSHHostKeyFingerprintArgsForCall) -} - -func (fake *FakeCloudControllerClient) AppSSHHostKeyFingerprintCalls(stub func() string) { - fake.appSSHHostKeyFingerprintMutex.Lock() - defer fake.appSSHHostKeyFingerprintMutex.Unlock() - fake.AppSSHHostKeyFingerprintStub = stub -} - -func (fake *FakeCloudControllerClient) AppSSHHostKeyFingerprintReturns(result1 string) { - fake.appSSHHostKeyFingerprintMutex.Lock() - defer fake.appSSHHostKeyFingerprintMutex.Unlock() - fake.AppSSHHostKeyFingerprintStub = nil - fake.appSSHHostKeyFingerprintReturns = struct { - result1 string - }{result1} -} - -func (fake *FakeCloudControllerClient) AppSSHHostKeyFingerprintReturnsOnCall(i int, result1 string) { - fake.appSSHHostKeyFingerprintMutex.Lock() - defer fake.appSSHHostKeyFingerprintMutex.Unlock() - fake.AppSSHHostKeyFingerprintStub = nil - if fake.appSSHHostKeyFingerprintReturnsOnCall == nil { - fake.appSSHHostKeyFingerprintReturnsOnCall = make(map[int]struct { - result1 string - }) - } - fake.appSSHHostKeyFingerprintReturnsOnCall[i] = struct { - result1 string - }{result1} -} - func (fake *FakeCloudControllerClient) ApplyOrganizationQuota(arg1 string, arg2 string) (resources.RelationshipList, ccv3.Warnings, error) { fake.applyOrganizationQuotaMutex.Lock() ret, specificReturn := fake.applyOrganizationQuotaReturnsOnCall[len(fake.applyOrganizationQuotaArgsForCall)] @@ -9214,73 +9073,6 @@ func (fake *FakeCloudControllerClient) GetProcessesReturnsOnCall(i int, result1 }{result1, result2, result3} } -func (fake *FakeCloudControllerClient) GetRevision(arg1 string) (resources.Revision, ccv3.Warnings, error) { - fake.getRevisionMutex.Lock() - ret, specificReturn := fake.getRevisionReturnsOnCall[len(fake.getRevisionArgsForCall)] - fake.getRevisionArgsForCall = append(fake.getRevisionArgsForCall, struct { - arg1 string - }{arg1}) - stub := fake.GetRevisionStub - fakeReturns := fake.getRevisionReturns - fake.recordInvocation("GetRevision", []interface{}{arg1}) - fake.getRevisionMutex.Unlock() - if stub != nil { - return stub(arg1) - } - if specificReturn { - return ret.result1, ret.result2, ret.result3 - } - return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 -} - -func (fake *FakeCloudControllerClient) GetRevisionCallCount() int { - fake.getRevisionMutex.RLock() - defer fake.getRevisionMutex.RUnlock() - return len(fake.getRevisionArgsForCall) -} - -func (fake *FakeCloudControllerClient) GetRevisionCalls(stub func(string) (resources.Revision, ccv3.Warnings, error)) { - fake.getRevisionMutex.Lock() - defer fake.getRevisionMutex.Unlock() - fake.GetRevisionStub = stub -} - -func (fake *FakeCloudControllerClient) GetRevisionArgsForCall(i int) string { - fake.getRevisionMutex.RLock() - defer fake.getRevisionMutex.RUnlock() - argsForCall := fake.getRevisionArgsForCall[i] - return argsForCall.arg1 -} - -func (fake *FakeCloudControllerClient) GetRevisionReturns(result1 resources.Revision, result2 ccv3.Warnings, result3 error) { - fake.getRevisionMutex.Lock() - defer fake.getRevisionMutex.Unlock() - fake.GetRevisionStub = nil - fake.getRevisionReturns = struct { - result1 resources.Revision - result2 ccv3.Warnings - result3 error - }{result1, result2, result3} -} - -func (fake *FakeCloudControllerClient) GetRevisionReturnsOnCall(i int, result1 resources.Revision, result2 ccv3.Warnings, result3 error) { - fake.getRevisionMutex.Lock() - defer fake.getRevisionMutex.Unlock() - fake.GetRevisionStub = nil - if fake.getRevisionReturnsOnCall == nil { - fake.getRevisionReturnsOnCall = make(map[int]struct { - result1 resources.Revision - result2 ccv3.Warnings - result3 error - }) - } - fake.getRevisionReturnsOnCall[i] = struct { - result1 resources.Revision - result2 ccv3.Warnings - result3 error - }{result1, result2, result3} -} - func (fake *FakeCloudControllerClient) GetRoles(arg1 ...ccv3.Query) ([]resources.Role, ccv3.IncludedResources, ccv3.Warnings, error) { fake.getRolesMutex.Lock() ret, specificReturn := fake.getRolesReturnsOnCall[len(fake.getRolesArgsForCall)] @@ -15248,10 +15040,6 @@ func (fake *FakeCloudControllerClient) WhoAmIReturnsOnCall(i int, result1 resour func (fake *FakeCloudControllerClient) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() - fake.appSSHEndpointMutex.RLock() - defer fake.appSSHEndpointMutex.RUnlock() - fake.appSSHHostKeyFingerprintMutex.RLock() - defer fake.appSSHHostKeyFingerprintMutex.RUnlock() fake.applyOrganizationQuotaMutex.RLock() defer fake.applyOrganizationQuotaMutex.RUnlock() fake.applySpaceQuotaMutex.RLock() @@ -15440,8 +15228,6 @@ func (fake *FakeCloudControllerClient) Invocations() map[string][][]interface{} defer fake.getProcessSidecarsMutex.RUnlock() fake.getProcessesMutex.RLock() defer fake.getProcessesMutex.RUnlock() - fake.getRevisionMutex.RLock() - defer fake.getRevisionMutex.RUnlock() fake.getRolesMutex.RLock() defer fake.getRolesMutex.RUnlock() fake.getRouteBindingsMutex.RLock() diff --git a/api/cloudcontroller/ccv3/internal/api_routes.go b/api/cloudcontroller/ccv3/internal/api_routes.go index 76a86b269c6..83d740b56af 100644 --- a/api/cloudcontroller/ccv3/internal/api_routes.go +++ b/api/cloudcontroller/ccv3/internal/api_routes.go @@ -15,15 +15,15 @@ const ( DeleteDomainRequest = "DeleteDomainRequest" DeleteIsolationSegmentRelationshipOrganizationRequest = "DeleteIsolationSegmentRelationshipOrganization" DeleteIsolationSegmentRequest = "DeleteIsolationSegment" - DeleteOrganizationQuotaRequest = "DeleteOrganizationQuota" DeleteOrganizationRequest = "DeleteOrganization" + DeleteOrganizationQuotaRequest = "DeleteOrganizationQuota" DeleteOrphanedRoutesRequest = "DeleteOrphanedRoutes" DeleteRoleRequest = "DeleteRoleRequest" DeleteRouteRequest = "DeleteRouteRequest" DeleteRouteBindingRequest = "DeleteRouteBinding" DeleteSecurityGroupRequest = "DeleteSecurityGroup" - DeleteSecurityGroupRunningSpaceRequest = "DeleteSecurityGroupRunningSpace" DeleteSecurityGroupStagingSpaceRequest = "DeleteSecurityGroupStagingSpace" + DeleteSecurityGroupRunningSpaceRequest = "DeleteSecurityGroupRunningSpace" DeleteServiceCredentialBindingRequest = "DeleteServiceCredentialBinding" DeleteServiceBrokerRequest = "DeleteServiceBrokerRequest" DeleteServiceInstanceRelationshipsSharedSpaceRequest = "DeleteServiceInstanceRelationshipsSharedSpace" @@ -31,9 +31,9 @@ const ( DeleteServiceOfferingRequest = "DeleteServiceOffering" DeleteServicePlanVisibilityRequest = "DeleteServicePlanVisibility" DeleteSharedOrgFromDomainRequest = "DeleteSharedOrgFromDomain" - DeleteSpaceQuotaFromSpaceRequest = "DeleteSpaceQuotaFromSpace" DeleteSpaceQuotaRequest = "DeleteSpaceQuota" DeleteSpaceRequest = "DeleteSpace" + DeleteSpaceQuotaFromSpaceRequest = "DeleteSpaceQuotaFromSpace" DeleteUserRequest = "DeleteUser" GetApplicationDropletCurrentRequest = "GetApplicationDropletCurrent" GetApplicationEnvRequest = "GetApplicationEnv" @@ -41,8 +41,8 @@ const ( GetApplicationManifestRequest = "GetApplicationManifest" GetApplicationProcessRequest = "GetApplicationProcess" GetApplicationProcessesRequest = "GetApplicationProcesses" - GetApplicationRevisionsDeployedRequest = "GetApplicationRevisionsDeployed" GetApplicationRevisionsRequest = "GetApplicationRevisions" + GetApplicationRevisionsDeployedRequest = "GetApplicationRevisionsDeployed" GetApplicationRoutesRequest = "GetApplicationRoutes" GetApplicationTasksRequest = "GetApplicationTasks" GetApplicationsRequest = "GetApplications" @@ -54,9 +54,9 @@ const ( GetDomainRequest = "GetDomain" GetDomainRouteReservationsRequest = "GetDomainRouteReservations" GetDomainsRequest = "GetDomains" - GetDropletBitsRequest = "GetDropletBits" GetDropletRequest = "GetDroplet" GetDropletsRequest = "GetDroplets" + GetDropletBitsRequest = "GetDropletBits" GetEnvironmentVariableGroupRequest = "GetEnvironmentVariableGroup" GetEventsRequest = "GetEvents" GetFeatureFlagRequest = "GetFeatureFlag" @@ -65,23 +65,22 @@ const ( GetIsolationSegmentRequest = "GetIsolationSegment" GetIsolationSegmentsRequest = "GetIsolationSegments" GetOrganizationDomainsRequest = "GetOrganizationDomains" - GetOrganizationQuotaRequest = "GetOrganizationQuota" GetOrganizationQuotasRequest = "GetOrganizationQuotas" + GetOrganizationQuotaRequest = "GetOrganizationQuota" GetOrganizationRelationshipDefaultIsolationSegmentRequest = "GetOrganizationRelationshipDefaultIsolationSegment" GetOrganizationRequest = "GetOrganization" GetOrganizationsRequest = "GetOrganizations" - GetPackageDropletsRequest = "GetPackageDroplets" GetPackageRequest = "GetPackage" GetPackagesRequest = "GetPackages" + GetPackageDropletsRequest = "GetPackageDroplets" GetProcessRequest = "GetProcess" - GetProcessSidecarsRequest = "GetProcessSidecars" - GetProcessStatsRequest = "GetProcessStats" GetProcessesRequest = "GetProcesses" + GetProcessStatsRequest = "GetProcessStats" + GetProcessSidecarsRequest = "GetProcessSidecars" GetRolesRequest = "GetRoles" GetRouteBindingsRequest = "GetRouteBindings" GetRouteDestinationsRequest = "GetRouteDestinations" GetRoutesRequest = "GetRoutes" - GetSSHEnabled = "GetSSHEnabled" GetSecurityGroupsRequest = "GetSecurityGroups" GetServiceBrokersRequest = "GetServiceBrokers" GetServiceCredentialBindingsRequest = "GetServiceCredentialBindings" @@ -93,15 +92,16 @@ const ( GetServiceOfferingRequest = "GetServiceOffering" GetServiceOfferingsRequest = "GetServiceOfferings" GetServicePlanRequest = "GetServicePlan" - GetServicePlanVisibilityRequest = "GetServicePlanVisibility" GetServicePlansRequest = "GetServicePlans" + GetServicePlanVisibilityRequest = "GetServicePlanVisibility" GetSpaceFeatureRequest = "GetSpaceFeatureRequest" - GetSpaceQuotaRequest = "GetSpaceQuota" - GetSpaceQuotasRequest = "GetSpaceQuotas" GetSpaceRelationshipIsolationSegmentRequest = "GetSpaceRelationshipIsolationSegment" GetSpaceRunningSecurityGroupsRequest = "GetSpaceRunningSecurityGroups" - GetSpaceStagingSecurityGroupsRequest = "GetSpaceStagingSecurityGroups" GetSpacesRequest = "GetSpaces" + GetSpaceQuotaRequest = "GetSpaceQuota" + GetSpaceQuotasRequest = "GetSpaceQuotas" + GetSpaceStagingSecurityGroupsRequest = "GetSpaceStagingSecurityGroups" + GetSSHEnabled = "GetSSHEnabled" GetStacksRequest = "GetStacks" GetTasksRequest = "GetTasks" GetTaskRequest = "GetTask" @@ -110,16 +110,16 @@ const ( MapRouteRequest = "MapRoute" PatchApplicationCurrentDropletRequest = "PatchApplicationCurrentDroplet" PatchApplicationEnvironmentVariablesRequest = "PatchApplicationEnvironmentVariables" - PatchApplicationFeaturesRequest = "PatchApplicationFeatures" PatchApplicationRequest = "PatchApplication" + PatchApplicationFeaturesRequest = "PatchApplicationFeatures" + PatchEnvironmentVariableGroupRequest = "PatchEnvironmentVariableGroup" PatchBuildpackRequest = "PatchBuildpack" PatchDestinationRequest = "PatchDestination" PatchDomainRequest = "PatchDomain" - PatchEnvironmentVariableGroupRequest = "PatchEnvironmentVariableGroup" PatchFeatureFlagRequest = "PatchFeatureFlag" - PatchOrganizationQuotaRequest = "PatchOrganizationQuota" PatchOrganizationRelationshipDefaultIsolationSegmentRequest = "PatchOrganizationRelationshipDefaultIsolationSegment" PatchOrganizationRequest = "PatchOrganization" + PatchOrganizationQuotaRequest = "PatchOrganizationQuota" PatchProcessRequest = "PatchProcess" PatchRouteRequest = "PatchRoute" PatchSecurityGroupRequest = "PatchSecurityGroup" @@ -127,10 +127,10 @@ const ( PatchServiceInstanceRequest = "PatchServiceInstance" PatchServiceOfferingRequest = "PatchServiceOfferingRequest" PatchServicePlanRequest = "PatchServicePlanRequest" - PatchSpaceFeaturesRequest = "PatchSpaceFeatures" - PatchSpaceQuotaRequest = "PatchSpaceQuota" PatchSpaceRelationshipIsolationSegmentRequest = "PatchSpaceRelationshipIsolationSegment" PatchSpaceRequest = "PatchSpace" + PatchSpaceFeaturesRequest = "PatchSpaceFeatures" + PatchSpaceQuotaRequest = "PatchSpaceQuota" PatchStackRequest = "PatchStack" PatchMoveRouteRequest = "PatchMoveRouteRequest" PostApplicationActionApplyManifest = "PostApplicationActionApplyM" @@ -151,18 +151,18 @@ const ( PostDropletRequest = "PostDroplet" PostIsolationSegmentRelationshipOrganizationsRequest = "PostIsolationSegmentRelationshipOrganizations" PostIsolationSegmentsRequest = "PostIsolationSegments" - PostOrganizationQuotaApplyRequest = "PostOrganizationQuotaApply" - PostOrganizationQuotaRequest = "PostOrganizationQuota" PostOrganizationRequest = "PostOrganization" - PostPackageBitsRequest = "PostPackageBits" + PostOrganizationQuotaRequest = "PostOrganizationQuota" + PostOrganizationQuotaApplyRequest = "PostOrganizationQuotaApply" PostPackageRequest = "PostPackage" + PostPackageBitsRequest = "PostPackageBits" PostResourceMatchesRequest = "PostResourceMatches" PostRoleRequest = "PostRole" PostRouteRequest = "PostRoute" PostRouteBindingRequest = "PostRouteBinding" PostSecurityGroupRequest = "PostSecurityGroup" - PostSecurityGroupRunningSpaceRequest = "PostSecurityGroupRunningSpace" PostSecurityGroupStagingSpaceRequest = "PostSecurityGroupStagingSpace" + PostSecurityGroupRunningSpaceRequest = "PostSecurityGroupRunningSpace" PostServiceCredentialBindingRequest = "PostServiceCredentialBinding" PostServiceBrokerRequest = "PostServiceBroker" PostServiceInstanceRequest = "PostServiceInstance" @@ -170,9 +170,9 @@ const ( PostServicePlanVisibilityRequest = "PostServicePlanVisibility" PostSpaceActionApplyManifestRequest = "PostSpaceActionApplyManifest" PostSpaceDiffManifestRequest = "PostSpaceDiffManifest" - PostSpaceQuotaRelationshipsRequest = "PostSpaceQuotaRelationships" - PostSpaceQuotaRequest = "PostSpaceQuota" PostSpaceRequest = "PostSpace" + PostSpaceQuotaRequest = "PostSpaceQuota" + PostSpaceQuotaRelationshipsRequest = "PostSpaceQuotaRelationships" PostUserRequest = "PostUser" PutTaskCancelRequest = "PutTaskCancel" SharePrivateDomainRequest = "SharePrivateDomainRequest" From b5409d10892bd367d82be38cc78dd37202d5670d Mon Sep 17 00:00:00 2001 From: Shwetha Gururaj Date: Fri, 3 Jan 2025 11:49:22 -0500 Subject: [PATCH 4/4] Clean up --- command/v7/revision_command.go | 40 +++++++++++++--------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/command/v7/revision_command.go b/command/v7/revision_command.go index 814eff0c5dc..c9e23af5464 100644 --- a/command/v7/revision_command.go +++ b/command/v7/revision_command.go @@ -59,34 +59,22 @@ func (cmd RevisionCommand) Execute(_ []string) error { if err != nil { return err } - isDeployed := revisionDeployed(revision, deployedRevisions) + isDeployed := cmd.revisionDeployed(revision, deployedRevisions) cmd.displayBasicRevisionInfo(revision, isDeployed) cmd.UI.DisplayNewline() cmd.UI.DisplayHeader("labels:") - labels := revision.Metadata.Labels - cmd.UI.DisplayWarnings(warnings) - if err != nil { - return err - } - - if len(labels) > 0 { - cmd.displayMetaData(labels) - cmd.UI.DisplayNewline() - } + cmd.displayMetaData(revision.Metadata.Labels) cmd.UI.DisplayHeader("annotations:") - annotations := revision.Metadata.Annotations - cmd.UI.DisplayWarnings(warnings) - - if len(annotations) > 0 { - cmd.displayMetaData(annotations) - cmd.UI.DisplayNewline() - } + cmd.displayMetaData(revision.Metadata.Annotations) cmd.UI.DisplayHeader("application environment variables:") - envVars, isPresent, warnings, _ := cmd.Actor.GetEnvironmentVariableGroupByRevision(revision) + envVars, isPresent, warnings, err := cmd.Actor.GetEnvironmentVariableGroupByRevision(revision) + if err != nil { + return err + } cmd.UI.DisplayWarnings(warnings) if isPresent { cmd.displayEnvVarGroup(envVars) @@ -118,15 +106,17 @@ func (cmd RevisionCommand) displayEnvVarGroup(envVarGroup v7action.EnvironmentVa } func (cmd RevisionCommand) displayMetaData(data map[string]types.NullString) { - tableData := [][]string{} - for k, v := range data { - tableData = append(tableData, []string{fmt.Sprintf("%s:", k), v.Value}) + if len(data) > 0 { + tableData := [][]string{} + for k, v := range data { + tableData = append(tableData, []string{fmt.Sprintf("%s:", k), v.Value}) + } + cmd.UI.DisplayKeyValueTable("", tableData, 3) + cmd.UI.DisplayNewline() } - cmd.UI.DisplayKeyValueTable("", tableData, 3) - } -func revisionDeployed(revision resources.Revision, deployedRevisions []resources.Revision) bool { +func (cmd RevisionCommand) revisionDeployed(revision resources.Revision, deployedRevisions []resources.Revision) bool { for _, deployedRevision := range deployedRevisions { if revision.GUID == deployedRevision.GUID { return true