diff --git a/actor/v7action/annotation.go b/actor/v7action/annotation.go new file mode 100644 index 00000000000..7b6afc03abc --- /dev/null +++ b/actor/v7action/annotation.go @@ -0,0 +1,23 @@ +package v7action + +import ( + "code.cloudfoundry.org/cli/resources" + "code.cloudfoundry.org/cli/types" +) + +func (actor *Actor) GetRevisionAnnotations(revisionGUID string) (map[string]types.NullString, Warnings, error) { + resource, warnings, err := actor.GetRevisionByGUID(revisionGUID) + return actor.extractAnnotations(resource.Metadata, warnings, err) +} + +func (actor *Actor) extractAnnotations(metadata *resources.Metadata, warnings Warnings, err error) (map[string]types.NullString, Warnings, error) { + var annotations map[string]types.NullString + + if err != nil { + return annotations, warnings, err + } + if metadata != nil { + annotations = metadata.Annotations + } + return annotations, warnings, nil +} diff --git a/actor/v7action/annotations_test.go b/actor/v7action/annotations_test.go new file mode 100644 index 00000000000..7fceaed030d --- /dev/null +++ b/actor/v7action/annotations_test.go @@ -0,0 +1,101 @@ +package v7action_test + +import ( + "errors" + + . "code.cloudfoundry.org/cli/actor/v7action" + "code.cloudfoundry.org/cli/actor/v7action/v7actionfakes" + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + "code.cloudfoundry.org/cli/resources" + "code.cloudfoundry.org/cli/types" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("annotations", func() { + var ( + actor *Actor + fakeCloudControllerClient *v7actionfakes.FakeCloudControllerClient + fakeSharedActor *v7actionfakes.FakeSharedActor + fakeConfig *v7actionfakes.FakeConfig + warnings Warnings + executeErr error + annotations map[string]types.NullString + ) + + BeforeEach(func() { + fakeCloudControllerClient = new(v7actionfakes.FakeCloudControllerClient) + fakeSharedActor = new(v7actionfakes.FakeSharedActor) + fakeConfig = new(v7actionfakes.FakeConfig) + actor = NewActor(fakeCloudControllerClient, fakeConfig, fakeSharedActor, nil, nil, nil) + }) + + Describe("GetRevisionAnnotations", func() { + JustBeforeEach(func() { + annotations, warnings, executeErr = actor.GetRevisionAnnotations("some-guid") + }) + + When("there are no client errors", func() { + BeforeEach(func() { + fakeCloudControllerClient.GetRevisionReturns( + resources.Revision{GUID: "some-guid"}, + ccv3.Warnings([]string{"warning-1", "warning-2"}), + nil, + ) + }) + + It("gets the revision", func() { + Expect(fakeCloudControllerClient.GetRevisionCallCount()).To(Equal(1)) + Expect(fakeCloudControllerClient.GetRevisionArgsForCall(0)).To(Equal("some-guid")) + }) + + When("there are no annotations on a revision", func() { + It("returns an empty map", func() { + Expect(executeErr).NotTo(HaveOccurred()) + Expect(warnings).To(ConsistOf("warning-1", "warning-2")) + Expect(annotations).To(BeEmpty()) + }) + }) + + When("there are annotations", func() { + var expectedAnnotations map[string]types.NullString + + BeforeEach(func() { + expectedAnnotations = map[string]types.NullString{"key1": types.NewNullString("value1"), "key2": types.NewNullString("value2")} + fakeCloudControllerClient.GetRevisionReturns( + resources.Revision{ + GUID: "some-guid", + Metadata: &resources.Metadata{ + Annotations: expectedAnnotations, + }, + }, + ccv3.Warnings([]string{"warning-1", "warning-2"}), + nil, + ) + }) + It("returns the annotations", func() { + Expect(executeErr).NotTo(HaveOccurred()) + Expect(warnings).To(ConsistOf("warning-1", "warning-2")) + Expect(annotations).To(Equal(expectedAnnotations)) + }) + }) + }) + + When("there is a client error", func() { + BeforeEach(func() { + fakeCloudControllerClient.GetRevisionReturns( + resources.Revision{GUID: "some-guid"}, + ccv3.Warnings([]string{"warning-1", "warning-2"}), + errors.New("get-revision-error"), + ) + }) + When("GetRevision fails", func() { + It("returns the error and all warnings", func() { + Expect(executeErr).To(HaveOccurred()) + Expect(warnings).To(ConsistOf("warning-1", "warning-2")) + Expect(executeErr).To(MatchError("get-revision-error")) + }) + }) + }) + }) +}) diff --git a/actor/v7action/cloud_controller_client.go b/actor/v7action/cloud_controller_client.go index 787a34f0d3c..facc8ccd6b3 100644 --- a/actor/v7action/cloud_controller_client.go +++ b/actor/v7action/cloud_controller_client.go @@ -109,6 +109,7 @@ type CloudControllerClient interface { 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) + GetRevision(revisionGUID string) (resources.Revision, 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) diff --git a/actor/v7action/label.go b/actor/v7action/label.go index 5ed9845e869..a03eb692fc5 100644 --- a/actor/v7action/label.go +++ b/actor/v7action/label.go @@ -61,6 +61,11 @@ func (actor *Actor) GetBuildpackLabels(buildpackName string, buildpackStack stri return actor.extractLabels(resource.Metadata, warnings, err) } +func (actor *Actor) GetRevisionLabels(revisionGUID string) (map[string]types.NullString, Warnings, error) { + resource, warnings, err := actor.GetRevisionByGUID(revisionGUID) + return actor.extractLabels(resource.Metadata, warnings, err) +} + func (actor *Actor) extractLabels(metadata *resources.Metadata, warnings Warnings, err error) (map[string]types.NullString, Warnings, error) { var labels map[string]types.NullString diff --git a/actor/v7action/label_test.go b/actor/v7action/label_test.go index bb5b8471b03..daf80d6e787 100644 --- a/actor/v7action/label_test.go +++ b/actor/v7action/label_test.go @@ -1470,4 +1470,73 @@ var _ = Describe("labels", func() { }) }) }) + + Describe("GetRevisionLabels", func() { + JustBeforeEach(func() { + labels, warnings, executeErr = actor.GetRevisionLabels("some-guid") + }) + + When("there are no client errors", func() { + BeforeEach(func() { + fakeCloudControllerClient.GetRevisionReturns( + resources.Revision{GUID: "some-guid"}, + ccv3.Warnings([]string{"warning-1", "warning-2"}), + nil, + ) + }) + + It("gets the revision", func() { + Expect(fakeCloudControllerClient.GetRevisionCallCount()).To(Equal(1)) + Expect(fakeCloudControllerClient.GetRevisionArgsForCall(0)).To(Equal("some-guid")) + }) + + When("there are no labels on a revision", func() { + It("returns an empty map", func() { + Expect(executeErr).NotTo(HaveOccurred()) + Expect(warnings).To(ConsistOf("warning-1", "warning-2")) + Expect(labels).To(BeEmpty()) + }) + }) + + When("there are labels", func() { + var expectedLabels map[string]types.NullString + + BeforeEach(func() { + expectedLabels = map[string]types.NullString{"key1": types.NewNullString("value1"), "key2": types.NewNullString("value2")} + fakeCloudControllerClient.GetRevisionReturns( + resources.Revision{ + GUID: "some-guid", + Metadata: &resources.Metadata{ + Labels: expectedLabels, + }, + }, + ccv3.Warnings([]string{"warning-1", "warning-2"}), + nil, + ) + }) + It("returns the labels", func() { + Expect(executeErr).NotTo(HaveOccurred()) + Expect(warnings).To(ConsistOf("warning-1", "warning-2")) + Expect(labels).To(Equal(expectedLabels)) + }) + }) + }) + + When("there is a client error", func() { + BeforeEach(func() { + fakeCloudControllerClient.GetRevisionReturns( + resources.Revision{GUID: "some-guid"}, + ccv3.Warnings([]string{"warning-1", "warning-2"}), + errors.New("get-revision-error"), + ) + }) + When("GetRevision fails", func() { + It("returns the error and all warnings", func() { + Expect(executeErr).To(HaveOccurred()) + Expect(warnings).To(ConsistOf("warning-1", "warning-2")) + Expect(executeErr).To(MatchError("get-revision-error")) + }) + }) + }) + }) }) diff --git a/actor/v7action/revision.go b/actor/v7action/revision.go new file mode 100644 index 00000000000..13301284b34 --- /dev/null +++ b/actor/v7action/revision.go @@ -0,0 +1,12 @@ +package v7action + +import "code.cloudfoundry.org/cli/resources" + +func (actor Actor) GetRevisionByGUID(revisionGUID string) (resources.Revision, Warnings, error) { + revision, warnings, err := actor.CloudControllerClient.GetRevision(revisionGUID) + if err != nil { + return resources.Revision{}, Warnings(warnings), err + } + + return resources.Revision(revision), Warnings(warnings), err +} diff --git a/actor/v7action/revision_test.go b/actor/v7action/revision_test.go new file mode 100644 index 00000000000..969e7269781 --- /dev/null +++ b/actor/v7action/revision_test.go @@ -0,0 +1,72 @@ +package v7action_test + +import ( + "errors" + + . "code.cloudfoundry.org/cli/actor/v7action" + "code.cloudfoundry.org/cli/actor/v7action/v7actionfakes" + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + "code.cloudfoundry.org/cli/resources" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Revision Actions", func() { + Describe("GetRevisionByGUID", func() { + var ( + actor *Actor + fakeCloudControllerClient *v7actionfakes.FakeCloudControllerClient + fakeConfig *v7actionfakes.FakeConfig + revisionGUID string + fetchedRevision resources.Revision + executeErr error + warnings Warnings + ) + + BeforeEach(func() { + fakeCloudControllerClient = new(v7actionfakes.FakeCloudControllerClient) + fakeConfig = new(v7actionfakes.FakeConfig) + actor = NewActor(fakeCloudControllerClient, fakeConfig, nil, nil, nil, nil) + revisionGUID = "revision-guid" + fakeConfig.APIVersionReturns("3.86.0") + }) + + JustBeforeEach(func() { + fetchedRevision, warnings, executeErr = actor.GetRevisionByGUID(revisionGUID) + }) + + When("finding the revision fails", func() { + BeforeEach(func() { + fakeCloudControllerClient.GetRevisionReturns( + resources.Revision{}, + ccv3.Warnings{"get-revision-warning"}, + errors.New("get-revision-error"), + ) + }) + + It("returns an executeError", func() { + Expect(executeErr).To(MatchError("get-revision-error")) + Expect(warnings).To(ConsistOf("get-revision-warning")) + }) + }) + + When("finding the revision succeeds", func() { + BeforeEach(func() { + fakeCloudControllerClient.GetRevisionReturns( + resources.Revision{GUID: "1", Deployable: true, Droplet: resources.Droplet{GUID: "droplet-guid-1"}}, + nil, + nil, + ) + }) + + It("returns the revision", func() { + Expect(fakeCloudControllerClient.GetRevisionCallCount()).To(Equal(1), "GetRevision call count") + Expect(fakeCloudControllerClient.GetRevisionArgsForCall(0)).To(Equal(revisionGUID)) + + Expect(fetchedRevision).To(Equal( + resources.Revision{GUID: "1", Deployable: true, Droplet: resources.Droplet{GUID: "droplet-guid-1"}})) + Expect(executeErr).ToNot(HaveOccurred()) + }) + }) + }) +}) diff --git a/actor/v7action/revisions_test.go b/actor/v7action/revisions_test.go index 8f387b75253..2a8c74368d4 100644 --- a/actor/v7action/revisions_test.go +++ b/actor/v7action/revisions_test.go @@ -460,7 +460,7 @@ var _ = Describe("Revisions Actions", func() { When("finding the environment variables succeeds", func() { BeforeEach(func() { fakeCloudControllerClient.GetEnvironmentVariablesByURLReturns( - ccv3.EnvironmentVariables{"foo": *types.NewFilteredString("bar")}, + resources.EnvironmentVariables{"foo": *types.NewFilteredString("bar")}, ccv3.Warnings{"get-env-vars-warning-1"}, nil, ) diff --git a/actor/v7action/v7actionfakes/fake_cloud_controller_client.go b/actor/v7action/v7actionfakes/fake_cloud_controller_client.go index c99b37f6dc8..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,18 +1121,18 @@ type FakeCloudControllerClient struct { result2 ccv3.Warnings result3 error } - GetEnvironmentVariablesByURLStub func(string) (ccv3.EnvironmentVariables, ccv3.Warnings, error) + GetEnvironmentVariablesByURLStub func(string) (resources.EnvironmentVariables, ccv3.Warnings, error) getEnvironmentVariablesByURLMutex sync.RWMutex getEnvironmentVariablesByURLArgsForCall []struct { arg1 string } getEnvironmentVariablesByURLReturns struct { - result1 ccv3.EnvironmentVariables + result1 resources.EnvironmentVariables result2 ccv3.Warnings result3 error } getEnvironmentVariablesByURLReturnsOnCall map[int]struct { - result1 ccv3.EnvironmentVariables + result1 resources.EnvironmentVariables result2 ccv3.Warnings result3 error } @@ -1432,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 { @@ -2782,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)] @@ -7612,21 +7753,22 @@ func (fake *FakeCloudControllerClient) GetEnvironmentVariableGroupReturnsOnCall( }{result1, result2, result3} } -func (fake *FakeCloudControllerClient) GetEnvironmentVariablesByURL(arg1 string) (ccv3.EnvironmentVariables, ccv3.Warnings, error) { +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 fake.GetEnvironmentVariablesByURLStub != nil { - return fake.GetEnvironmentVariablesByURLStub(arg1) + if stub != nil { + return stub(arg1) } if specificReturn { return ret.result1, ret.result2, ret.result3 } - fakeReturns := fake.getEnvironmentVariablesByURLReturns return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 } @@ -7636,7 +7778,7 @@ func (fake *FakeCloudControllerClient) GetEnvironmentVariablesByURLCallCount() i return len(fake.getEnvironmentVariablesByURLArgsForCall) } -func (fake *FakeCloudControllerClient) GetEnvironmentVariablesByURLCalls(stub func(string) (ccv3.EnvironmentVariables, ccv3.Warnings, error)) { +func (fake *FakeCloudControllerClient) GetEnvironmentVariablesByURLCalls(stub func(string) (resources.EnvironmentVariables, ccv3.Warnings, error)) { fake.getEnvironmentVariablesByURLMutex.Lock() defer fake.getEnvironmentVariablesByURLMutex.Unlock() fake.GetEnvironmentVariablesByURLStub = stub @@ -7649,30 +7791,30 @@ func (fake *FakeCloudControllerClient) GetEnvironmentVariablesByURLArgsForCall(i return argsForCall.arg1 } -func (fake *FakeCloudControllerClient) GetEnvironmentVariablesByURLReturns(result1 ccv3.EnvironmentVariables, result2 ccv3.Warnings, result3 error) { +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 ccv3.EnvironmentVariables + result1 resources.EnvironmentVariables result2 ccv3.Warnings result3 error }{result1, result2, result3} } -func (fake *FakeCloudControllerClient) GetEnvironmentVariablesByURLReturnsOnCall(i int, result1 ccv3.EnvironmentVariables, result2 ccv3.Warnings, result3 error) { +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 ccv3.EnvironmentVariables + result1 resources.EnvironmentVariables result2 ccv3.Warnings result3 error }) } fake.getEnvironmentVariablesByURLReturnsOnCall[i] = struct { - result1 ccv3.EnvironmentVariables + result1 resources.EnvironmentVariables result2 ccv3.Warnings result3 error }{result1, result2, result3} @@ -9072,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)] @@ -15039,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() @@ -15227,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 9b200603fb9..2e95e6a0ad4 100644 --- a/api/cloudcontroller/ccv3/internal/api_routes.go +++ b/api/cloudcontroller/ccv3/internal/api_routes.go @@ -77,6 +77,7 @@ const ( GetProcessSidecarsRequest = "GetProcessSidecars" GetProcessStatsRequest = "GetProcessStats" GetProcessesRequest = "GetProcesses" + GetRevisionRequest = "GetRevision" GetRolesRequest = "GetRoles" GetRouteBindingsRequest = "GetRouteBindings" GetRouteDestinationsRequest = "GetRouteDestinations" @@ -272,6 +273,7 @@ var APIRoutes = map[string]Route{ GetProcessStatsRequest: {Path: "/v3/processes/:process_guid/stats", Method: http.MethodGet}, GetProcessSidecarsRequest: {Path: "/v3/processes/:process_guid/sidecars", Method: http.MethodGet}, PostResourceMatchesRequest: {Path: "/v3/resource_matches", Method: http.MethodPost}, + GetRevisionRequest: {Path: "/v3/revisions/:revision_guid", Method: http.MethodGet}, GetRolesRequest: {Path: "/v3/roles", Method: http.MethodGet}, PostRoleRequest: {Path: "/v3/roles", Method: http.MethodPost}, DeleteRoleRequest: {Path: "/v3/roles/:role_guid", Method: http.MethodDelete}, diff --git a/api/cloudcontroller/ccv3/revision.go b/api/cloudcontroller/ccv3/revision.go new file mode 100644 index 00000000000..a89530cccfc --- /dev/null +++ b/api/cloudcontroller/ccv3/revision.go @@ -0,0 +1,18 @@ +package ccv3 + +import ( + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/internal" + "code.cloudfoundry.org/cli/resources" +) + +func (client *Client) GetRevision(revisionGUID string) (resources.Revision, Warnings, error) { + var revision resources.Revision + + _, warnings, err := client.MakeRequest(RequestParams{ + RequestName: internal.GetRevisionRequest, + URIParams: internal.Params{"revision_guid": revisionGUID}, + ResponseBody: &revision, + }) + + return revision, warnings, err +} diff --git a/api/cloudcontroller/ccv3/revision_test.go b/api/cloudcontroller/ccv3/revision_test.go new file mode 100644 index 00000000000..3938c757c40 --- /dev/null +++ b/api/cloudcontroller/ccv3/revision_test.go @@ -0,0 +1,87 @@ +package ccv3_test + +import ( + "net/http" + + "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + . "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/ccv3fakes" + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/internal" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Revision", func() { + var ( + client *Client + requester *ccv3fakes.FakeRequester + ) + + BeforeEach(func() { + requester = new(ccv3fakes.FakeRequester) + client, _ = NewFakeRequesterTestClient(requester) + }) + + Describe("GetRevision", func() { + var ( + warnings Warnings + executeErr error + ) + + JustBeforeEach(func() { + _, warnings, executeErr = client.GetRevision("some-revision-guid") + }) + + 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", + }, + } + + requester.MakeRequestReturns( + "fake-job-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", + }, + }, + })) + Expect(warnings).To(ConsistOf("this is a warning")) + }) + }) + + When("revision exists", func() { + BeforeEach(func() { + requester.MakeRequestCalls(func(requestParams RequestParams) (ccv3.JobURL, Warnings, error) { + requestParams.URIParams = internal.Params{"revision_guid": "some-revision-guid"} + return JobURL(""), Warnings{"this is a warning", "this is another warning"}, nil + }) + }) + + It("returns the revision and all warnings", func() { + Expect(requester.MakeRequestCallCount()).To(Equal(1)) + actualParams := requester.MakeRequestArgsForCall(0) + Expect(actualParams.RequestName).To(Equal(internal.GetRevisionRequest)) + + Expect(executeErr).NotTo(HaveOccurred()) + Expect(warnings).To(ConsistOf("this is a warning", "this is another warning")) + Expect(actualParams.URIParams).To(Equal(internal.Params{"revision_guid": "some-revision-guid"})) + }) + }) + }) +}) diff --git a/api/cloudcontroller/ccv3/revisions_test.go b/api/cloudcontroller/ccv3/revisions_test.go index 0382e657112..afeb59fbb7e 100644 --- a/api/cloudcontroller/ccv3/revisions_test.go +++ b/api/cloudcontroller/ccv3/revisions_test.go @@ -4,7 +4,6 @@ import ( "net/http" "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" - "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" . "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/ccv3fakes" "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/internal" @@ -140,7 +139,7 @@ var _ = Describe("Revisions", func() { var ( warnings Warnings executeErr error - environmentVariables ccv3.EnvironmentVariables + environmentVariables resources.EnvironmentVariables ) JustBeforeEach(func() { @@ -192,7 +191,7 @@ var _ = Describe("Revisions", func() { When("revision exist", func() { BeforeEach(func() { requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { - (*requestParams.ResponseBody.(*ccv3.EnvironmentVariables))["foo"] = *types.NewFilteredString("bar") + (*requestParams.ResponseBody.(*resources.EnvironmentVariables))["foo"] = *types.NewFilteredString("bar") return "url", Warnings{"this is a warning"}, nil }) }) diff --git a/command/v7/actor.go b/command/v7/actor.go index d75347909ed..c8503da6d12 100644 --- a/command/v7/actor.go +++ b/command/v7/actor.go @@ -137,6 +137,8 @@ type Actor interface { GetRootResponse() (v7action.Info, v7action.Warnings, error) GetRevisionByApplicationAndVersion(appGUID string, revisionVersion int) (resources.Revision, v7action.Warnings, error) GetRevisionsByApplicationNameAndSpace(appName string, spaceGUID string) ([]resources.Revision, v7action.Warnings, error) + GetRevisionLabels(revisionGUID string) (map[string]types.NullString, v7action.Warnings, error) + GetRevisionAnnotations(revisionGUID string) (map[string]types.NullString, v7action.Warnings, error) GetRouteByAttributes(domain resources.Domain, hostname string, path string, port int) (resources.Route, v7action.Warnings, error) GetRouteDestinationByAppGUID(route resources.Route, appGUID string) (resources.RouteDestination, error) GetRouteLabels(routeName string, spaceGUID string) (map[string]types.NullString, v7action.Warnings, error) diff --git a/command/v7/revision_command.go b/command/v7/revision_command.go index 8df6e5de1da..913fecefde2 100644 --- a/command/v7/revision_command.go +++ b/command/v7/revision_command.go @@ -5,9 +5,9 @@ import ( "strconv" "code.cloudfoundry.org/cli/actor/v7action" - "code.cloudfoundry.org/cli/command" "code.cloudfoundry.org/cli/command/flag" "code.cloudfoundry.org/cli/resources" + "code.cloudfoundry.org/cli/types" ) type RevisionCommand struct { @@ -19,8 +19,6 @@ type RevisionCommand struct { } func (cmd RevisionCommand) Execute(_ []string) error { - cmd.UI.DisplayWarning(command.ExperimentalWarning) - cmd.UI.DisplayNewline() err := cmd.SharedActor.CheckTarget(true, true) if err != nil { return err @@ -49,12 +47,30 @@ func (cmd RevisionCommand) Execute(_ []string) error { ) isDeployed := revisionDeployed(revision, deployedRevisions) - envVars, isPresent, warnings, _ := cmd.Actor.GetEnvironmentVariableGroupByRevision(revision) - cmd.UI.DisplayWarnings(warnings) - cmd.displayBasicRevisionInfo(revision, isDeployed) cmd.UI.DisplayNewline() + cmd.UI.DisplayHeader("labels:") + labels, warnings, err := cmd.Actor.GetRevisionLabels(revision.GUID) + cmd.UI.DisplayWarnings(warnings) + + if len(labels) > 0 { + cmd.displayMetaData(labels) + cmd.UI.DisplayNewline() + } + + cmd.UI.DisplayHeader("annotations:") + annotations, warnings, err := cmd.Actor.GetRevisionAnnotations(revision.GUID) + 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() @@ -81,10 +97,18 @@ func (cmd RevisionCommand) displayEnvVarGroup(envVarGroup v7action.EnvironmentVa for k, v := range envVarGroup { envVarTable = append(envVarTable, []string{fmt.Sprintf("%s:", k), v.Value}) } - cmd.UI.DisplayText("application environment variables:") 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 { diff --git a/command/v7/revision_command_test.go b/command/v7/revision_command_test.go index 648356b45a6..c6dcbb6d30d 100644 --- a/command/v7/revision_command_test.go +++ b/command/v7/revision_command_test.go @@ -55,11 +55,7 @@ var _ = Describe("revision Command", func() { }) JustBeforeEach(func() { - Expect(cmd.Execute(nil)).To(Succeed()) - }) - - It("displays the experimental warning", func() { - Expect(testUI.Err).To(Say("This command is in EXPERIMENTAL stage and may change without notice")) + executeErr = cmd.Execute(nil) }) When("checking target fails", func() { @@ -121,6 +117,14 @@ var _ = Describe("revision Command", func() { 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) @@ -129,6 +133,12 @@ var _ = Describe("revision Command", func() { environmentVariableGroup := v7action.EnvironmentVariableGroup{ "foo": *types.NewFilteredString("bar3"), } + labels := map[string]types.NullString{ + "label": types.NewNullString("foo3"), + } + annotations := map[string]types.NullString{ + "annotation": types.NewNullString("foo3"), + } fakeActor.GetEnvironmentVariableGroupByRevisionReturns( environmentVariableGroup, true, @@ -136,6 +146,8 @@ var _ = Describe("revision Command", func() { nil, ) + fakeActor.GetRevisionLabelsReturns(labels, nil, nil) + fakeActor.GetRevisionAnnotationsReturns(annotations, nil, nil) cmd.Version = flag.Revision{NullInt: types.NullInt{Value: 3, IsSet: true}} }) @@ -177,17 +189,18 @@ var _ = Describe("revision Command", func() { 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`)) - // 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`)) }) - When("there is not a environment_variables link provided", func() { + When("there are no environment_variables link and metadata provided", func() { BeforeEach(func() { revision = resources.Revision{ Version: 3, @@ -208,7 +221,9 @@ var _ = Describe("revision Command", func() { 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).ToNot(Say("application environment variables:")) + Expect(testUI.Out).To(Say("labels:")) + Expect(testUI.Out).To(Say("annotations:")) + Expect(testUI.Out).To(Say("application environment variables:")) }) }) diff --git a/command/v7/v7fakes/fake_actor.go b/command/v7/v7fakes/fake_actor.go index 55d9a8c227d..543c0eefa79 100644 --- a/command/v7/v7fakes/fake_actor.go +++ b/command/v7/v7fakes/fake_actor.go @@ -1720,6 +1720,21 @@ type FakeActor struct { result2 v7action.Warnings result3 error } + GetRevisionAnnotationsStub func(string) (map[string]types.NullString, v7action.Warnings, error) + getRevisionAnnotationsMutex sync.RWMutex + getRevisionAnnotationsArgsForCall []struct { + arg1 string + } + getRevisionAnnotationsReturns struct { + result1 map[string]types.NullString + result2 v7action.Warnings + result3 error + } + getRevisionAnnotationsReturnsOnCall map[int]struct { + result1 map[string]types.NullString + result2 v7action.Warnings + result3 error + } GetRevisionByApplicationAndVersionStub func(string, int) (resources.Revision, v7action.Warnings, error) getRevisionByApplicationAndVersionMutex sync.RWMutex getRevisionByApplicationAndVersionArgsForCall []struct { @@ -1736,6 +1751,21 @@ type FakeActor struct { result2 v7action.Warnings result3 error } + GetRevisionLabelsStub func(string) (map[string]types.NullString, v7action.Warnings, error) + getRevisionLabelsMutex sync.RWMutex + getRevisionLabelsArgsForCall []struct { + arg1 string + } + getRevisionLabelsReturns struct { + result1 map[string]types.NullString + result2 v7action.Warnings + result3 error + } + getRevisionLabelsReturnsOnCall map[int]struct { + result1 map[string]types.NullString + result2 v7action.Warnings + result3 error + } GetRevisionsByApplicationNameAndSpaceStub func(string, string) ([]resources.Revision, v7action.Warnings, error) getRevisionsByApplicationNameAndSpaceMutex sync.RWMutex getRevisionsByApplicationNameAndSpaceArgsForCall []struct { @@ -9424,15 +9454,16 @@ func (fake *FakeActor) GetEnvironmentVariableGroupByRevision(arg1 resources.Revi 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 fake.GetEnvironmentVariableGroupByRevisionStub != nil { - return fake.GetEnvironmentVariableGroupByRevisionStub(arg1) + if stub != nil { + return stub(arg1) } if specificReturn { return ret.result1, ret.result2, ret.result3, ret.result4 } - fakeReturns := fake.getEnvironmentVariableGroupByRevisionReturns return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3, fakeReturns.result4 } @@ -11119,6 +11150,73 @@ func (fake *FakeActor) GetRecentLogsForApplicationByNameAndSpaceReturnsOnCall(i }{result1, result2, result3} } +func (fake *FakeActor) GetRevisionAnnotations(arg1 string) (map[string]types.NullString, v7action.Warnings, error) { + fake.getRevisionAnnotationsMutex.Lock() + ret, specificReturn := fake.getRevisionAnnotationsReturnsOnCall[len(fake.getRevisionAnnotationsArgsForCall)] + fake.getRevisionAnnotationsArgsForCall = append(fake.getRevisionAnnotationsArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.GetRevisionAnnotationsStub + fakeReturns := fake.getRevisionAnnotationsReturns + fake.recordInvocation("GetRevisionAnnotations", []interface{}{arg1}) + fake.getRevisionAnnotationsMutex.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 *FakeActor) GetRevisionAnnotationsCallCount() int { + fake.getRevisionAnnotationsMutex.RLock() + defer fake.getRevisionAnnotationsMutex.RUnlock() + return len(fake.getRevisionAnnotationsArgsForCall) +} + +func (fake *FakeActor) GetRevisionAnnotationsCalls(stub func(string) (map[string]types.NullString, v7action.Warnings, error)) { + fake.getRevisionAnnotationsMutex.Lock() + defer fake.getRevisionAnnotationsMutex.Unlock() + fake.GetRevisionAnnotationsStub = stub +} + +func (fake *FakeActor) GetRevisionAnnotationsArgsForCall(i int) string { + fake.getRevisionAnnotationsMutex.RLock() + defer fake.getRevisionAnnotationsMutex.RUnlock() + argsForCall := fake.getRevisionAnnotationsArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeActor) GetRevisionAnnotationsReturns(result1 map[string]types.NullString, result2 v7action.Warnings, result3 error) { + fake.getRevisionAnnotationsMutex.Lock() + defer fake.getRevisionAnnotationsMutex.Unlock() + fake.GetRevisionAnnotationsStub = nil + fake.getRevisionAnnotationsReturns = struct { + result1 map[string]types.NullString + result2 v7action.Warnings + result3 error + }{result1, result2, result3} +} + +func (fake *FakeActor) GetRevisionAnnotationsReturnsOnCall(i int, result1 map[string]types.NullString, result2 v7action.Warnings, result3 error) { + fake.getRevisionAnnotationsMutex.Lock() + defer fake.getRevisionAnnotationsMutex.Unlock() + fake.GetRevisionAnnotationsStub = nil + if fake.getRevisionAnnotationsReturnsOnCall == nil { + fake.getRevisionAnnotationsReturnsOnCall = make(map[int]struct { + result1 map[string]types.NullString + result2 v7action.Warnings + result3 error + }) + } + fake.getRevisionAnnotationsReturnsOnCall[i] = struct { + result1 map[string]types.NullString + result2 v7action.Warnings + result3 error + }{result1, result2, result3} +} + func (fake *FakeActor) GetRevisionByApplicationAndVersion(arg1 string, arg2 int) (resources.Revision, v7action.Warnings, error) { fake.getRevisionByApplicationAndVersionMutex.Lock() ret, specificReturn := fake.getRevisionByApplicationAndVersionReturnsOnCall[len(fake.getRevisionByApplicationAndVersionArgsForCall)] @@ -11187,6 +11285,73 @@ func (fake *FakeActor) GetRevisionByApplicationAndVersionReturnsOnCall(i int, re }{result1, result2, result3} } +func (fake *FakeActor) GetRevisionLabels(arg1 string) (map[string]types.NullString, v7action.Warnings, error) { + fake.getRevisionLabelsMutex.Lock() + ret, specificReturn := fake.getRevisionLabelsReturnsOnCall[len(fake.getRevisionLabelsArgsForCall)] + fake.getRevisionLabelsArgsForCall = append(fake.getRevisionLabelsArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.GetRevisionLabelsStub + fakeReturns := fake.getRevisionLabelsReturns + fake.recordInvocation("GetRevisionLabels", []interface{}{arg1}) + fake.getRevisionLabelsMutex.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 *FakeActor) GetRevisionLabelsCallCount() int { + fake.getRevisionLabelsMutex.RLock() + defer fake.getRevisionLabelsMutex.RUnlock() + return len(fake.getRevisionLabelsArgsForCall) +} + +func (fake *FakeActor) GetRevisionLabelsCalls(stub func(string) (map[string]types.NullString, v7action.Warnings, error)) { + fake.getRevisionLabelsMutex.Lock() + defer fake.getRevisionLabelsMutex.Unlock() + fake.GetRevisionLabelsStub = stub +} + +func (fake *FakeActor) GetRevisionLabelsArgsForCall(i int) string { + fake.getRevisionLabelsMutex.RLock() + defer fake.getRevisionLabelsMutex.RUnlock() + argsForCall := fake.getRevisionLabelsArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeActor) GetRevisionLabelsReturns(result1 map[string]types.NullString, result2 v7action.Warnings, result3 error) { + fake.getRevisionLabelsMutex.Lock() + defer fake.getRevisionLabelsMutex.Unlock() + fake.GetRevisionLabelsStub = nil + fake.getRevisionLabelsReturns = struct { + result1 map[string]types.NullString + result2 v7action.Warnings + result3 error + }{result1, result2, result3} +} + +func (fake *FakeActor) GetRevisionLabelsReturnsOnCall(i int, result1 map[string]types.NullString, result2 v7action.Warnings, result3 error) { + fake.getRevisionLabelsMutex.Lock() + defer fake.getRevisionLabelsMutex.Unlock() + fake.GetRevisionLabelsStub = nil + if fake.getRevisionLabelsReturnsOnCall == nil { + fake.getRevisionLabelsReturnsOnCall = make(map[int]struct { + result1 map[string]types.NullString + result2 v7action.Warnings + result3 error + }) + } + fake.getRevisionLabelsReturnsOnCall[i] = struct { + result1 map[string]types.NullString + result2 v7action.Warnings + result3 error + }{result1, result2, result3} +} + func (fake *FakeActor) GetRevisionsByApplicationNameAndSpace(arg1 string, arg2 string) ([]resources.Revision, v7action.Warnings, error) { fake.getRevisionsByApplicationNameAndSpaceMutex.Lock() ret, specificReturn := fake.getRevisionsByApplicationNameAndSpaceReturnsOnCall[len(fake.getRevisionsByApplicationNameAndSpaceArgsForCall)] @@ -19788,8 +19953,12 @@ func (fake *FakeActor) Invocations() map[string][][]interface{} { defer fake.getRecentEventsByApplicationNameAndSpaceMutex.RUnlock() fake.getRecentLogsForApplicationByNameAndSpaceMutex.RLock() defer fake.getRecentLogsForApplicationByNameAndSpaceMutex.RUnlock() + fake.getRevisionAnnotationsMutex.RLock() + defer fake.getRevisionAnnotationsMutex.RUnlock() fake.getRevisionByApplicationAndVersionMutex.RLock() defer fake.getRevisionByApplicationAndVersionMutex.RUnlock() + fake.getRevisionLabelsMutex.RLock() + defer fake.getRevisionLabelsMutex.RUnlock() fake.getRevisionsByApplicationNameAndSpaceMutex.RLock() defer fake.getRevisionsByApplicationNameAndSpaceMutex.RUnlock() fake.getRootResponseMutex.RLock() diff --git a/integration/v7/isolated/revision_command_test.go b/integration/v7/isolated/revision_command_test.go index 9502ccdeba8..a88c61c7d6f 100644 --- a/integration/v7/isolated/revision_command_test.go +++ b/integration/v7/isolated/revision_command_test.go @@ -1,7 +1,11 @@ package isolated import ( + "bytes" + "encoding/json" "fmt" + "os/exec" + "strings" . "code.cloudfoundry.org/cli/cf/util/testhelpers/matchers" "code.cloudfoundry.org/cli/integration/helpers" @@ -11,7 +15,7 @@ import ( . "github.com/onsi/gomega/gexec" ) -var _ = Describe("revision command", func() { +var _ = FDescribe("revision command", func() { var ( orgName string spaceName string @@ -57,7 +61,7 @@ var _ = Describe("revision command", func() { }) AfterEach(func() { - helpers.QuickDeleteOrg(orgName) + // helpers.QuickDeleteOrg(orgName) }) // TODO: #173456870 when app not provided, app does not exist, revision doesn't exist cases @@ -72,7 +76,28 @@ var _ = Describe("revision command", func() { }) }) - FIt("shows details about the revision", func() { + 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)) @@ -87,11 +112,11 @@ var _ = Describe("revision command", func() { 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: foo1`)) + Expect(session).Should(Say(`labels:`)) + Expect(session).Should(Say(`label: foo3`)) - // Expect(session).Should(Say(`annotations:`)) - // Expect(session).Should(Say(`annotation: foo1`)) + Expect(session).Should(Say(`annotations:`)) + Expect(session).Should(Say(`annotation: foo3`)) Expect(session).Should(Say(`application environment variables:`)) Expect(session).Should(Say(`foo: bar1`)) @@ -109,14 +134,11 @@ var _ = Describe("revision command", func() { 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`)) - - // Expect(session).Should(Say(`labels:`)) - // Expect(session).Should(Say(`label: foo2`)) - - // Expect(session).Should(Say(`annotations:`)) - // Expect(session).Should(Say(`annotation: foo2`)) }) }) }) 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 ae000e4145a..e187c951f01 100644 --- a/resources/revision_resource.go +++ b/resources/revision_resource.go @@ -1,12 +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"` - Links APILinks `json:"links"` + 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"` }