-
Notifications
You must be signed in to change notification settings - Fork 933
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add 'create-service-key' command in cli
1. Add a new command named "create-service-key" to create a service key for a specified service instance. 2. Add error of unbindable service [finishes #87057732 & #87157018] Signed-off-by: zhang-hua <zhuadl@cn.ibm.com>
- Loading branch information
Showing
21 changed files
with
847 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package fakes | ||
|
||
type FakeServiceKeyRepo struct { | ||
CreateServiceKeyError error | ||
|
||
CreateServiceKeyArgs CreateServiceKeyArgsType | ||
} | ||
|
||
type CreateServiceKeyArgsType struct { | ||
ServiceInstanceId string | ||
ServiceKeyName string | ||
} | ||
|
||
func NewFakeServiceKeyRepo() *FakeServiceKeyRepo { | ||
return &FakeServiceKeyRepo{ | ||
CreateServiceKeyArgs: CreateServiceKeyArgsType{}, | ||
} | ||
} | ||
|
||
func (f *FakeServiceKeyRepo) CreateServiceKey(instanceId string, serviceKeyName string) (apiErr error) { | ||
f.CreateServiceKeyArgs.ServiceInstanceId = instanceId | ||
f.CreateServiceKeyArgs.ServiceKeyName = serviceKeyName | ||
|
||
return f.CreateServiceKeyError | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package api | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/cloudfoundry/cli/cf/configuration/core_config" | ||
"github.com/cloudfoundry/cli/cf/errors" | ||
"github.com/cloudfoundry/cli/cf/net" | ||
) | ||
|
||
type ServiceKeyRepository interface { | ||
CreateServiceKey(instanceId string, keyName string) (apiErr error) | ||
} | ||
|
||
type CloudControllerServiceKeyRepository struct { | ||
config core_config.Reader | ||
gateway net.Gateway | ||
} | ||
|
||
func NewCloudControllerServiceKeyRepository(config core_config.Reader, gateway net.Gateway) (repo CloudControllerServiceKeyRepository) { | ||
return CloudControllerServiceKeyRepository{ | ||
config: config, | ||
gateway: gateway, | ||
} | ||
} | ||
|
||
func (c CloudControllerServiceKeyRepository) CreateServiceKey(instanceId string, keyName string) (apiErr error) { | ||
path := "/v2/service_keys" | ||
data := fmt.Sprintf(`{"service_instance_guid":"%s","name":"%s"}`, instanceId, keyName) | ||
|
||
err := c.gateway.CreateResource(c.config.ApiEndpoint(), path, strings.NewReader(data)) | ||
|
||
if httpErr, ok := err.(errors.HttpError); ok && httpErr.ErrorCode() == errors.SERVICE_KEY_NAME_TAKEN { | ||
return errors.NewModelAlreadyExistsError("Service key", keyName) | ||
} else if httpErr, ok := err.(errors.HttpError); ok && httpErr.ErrorCode() == errors.UNBINDABLE_SERVICE { | ||
return errors.NewUnbindableServiceError() | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package api_test | ||
|
||
import ( | ||
"net/http" | ||
"net/http/httptest" | ||
"time" | ||
|
||
testapi "github.com/cloudfoundry/cli/cf/api/fakes" | ||
testconfig "github.com/cloudfoundry/cli/testhelpers/configuration" | ||
testnet "github.com/cloudfoundry/cli/testhelpers/net" | ||
testterm "github.com/cloudfoundry/cli/testhelpers/terminal" | ||
|
||
"github.com/cloudfoundry/cli/cf/configuration/core_config" | ||
"github.com/cloudfoundry/cli/cf/errors" | ||
"github.com/cloudfoundry/cli/cf/net" | ||
|
||
. "github.com/cloudfoundry/cli/cf/api" | ||
. "github.com/cloudfoundry/cli/testhelpers/matchers" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
var _ = Describe("Service Keys Repo", func() { | ||
var ( | ||
testServer *httptest.Server | ||
testHandler *testnet.TestHandler | ||
configRepo core_config.ReadWriter | ||
repo ServiceKeyRepository | ||
) | ||
|
||
setupTestServer := func(reqs ...testnet.TestRequest) { | ||
testServer, testHandler = testnet.NewServer(reqs) | ||
configRepo.SetApiEndpoint(testServer.URL) | ||
} | ||
|
||
BeforeEach(func() { | ||
configRepo = testconfig.NewRepositoryWithDefaults() | ||
configRepo.SetAccessToken("BEARER my_access_token") | ||
|
||
gateway := net.NewCloudControllerGateway(configRepo, time.Now, &testterm.FakeUI{}) | ||
repo = NewCloudControllerServiceKeyRepository(configRepo, gateway) | ||
}) | ||
|
||
Describe("creating a service key", func() { | ||
It("makes the right request", func() { | ||
setupTestServer(testapi.NewCloudControllerTestRequest(testnet.TestRequest{ | ||
Method: "POST", | ||
Path: "/v2/service_keys", | ||
Matcher: testnet.RequestBodyMatcher(`{"service_instance_guid": "fake-instance-guid", "name": "fake-key-name"}`), | ||
Response: testnet.TestResponse{Status: http.StatusCreated}, | ||
})) | ||
|
||
err := repo.CreateServiceKey("fake-instance-guid", "fake-key-name") | ||
Expect(testHandler).To(HaveAllRequestsCalled()) | ||
Expect(err).NotTo(HaveOccurred()) | ||
}) | ||
|
||
It("returns a ModelAlreadyExistsError if the service key exists", func() { | ||
setupTestServer(testapi.NewCloudControllerTestRequest(testnet.TestRequest{ | ||
Method: "POST", | ||
Path: "/v2/service_keys", | ||
Matcher: testnet.RequestBodyMatcher(`{"service_instance_guid":"fake-instance-guid","name":"exist-service-key"}`), | ||
Response: testnet.TestResponse{ | ||
Status: http.StatusBadRequest, | ||
Body: `{"code":60003,"description":"The service key name is taken: exist-service-key"}`}, | ||
})) | ||
|
||
err := repo.CreateServiceKey("fake-instance-guid", "exist-service-key") | ||
Expect(testHandler).To(HaveAllRequestsCalled()) | ||
Expect(err).To(BeAssignableToTypeOf(&errors.ModelAlreadyExistsError{})) | ||
}) | ||
}) | ||
|
||
AfterEach(func() { | ||
testServer.Close() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package servicekey | ||
|
||
import ( | ||
"github.com/cloudfoundry/cli/cf/api" | ||
"github.com/cloudfoundry/cli/cf/command_metadata" | ||
"github.com/cloudfoundry/cli/cf/configuration/core_config" | ||
"github.com/cloudfoundry/cli/cf/errors" | ||
"github.com/cloudfoundry/cli/cf/requirements" | ||
"github.com/cloudfoundry/cli/cf/terminal" | ||
"github.com/codegangsta/cli" | ||
|
||
. "github.com/cloudfoundry/cli/cf/i18n" | ||
) | ||
|
||
type CreateServiceKey struct { | ||
ui terminal.UI | ||
config core_config.Reader | ||
serviceRepo api.ServiceRepository | ||
serviceKeyRepo api.ServiceKeyRepository | ||
} | ||
|
||
func NewCreateServiceKey(ui terminal.UI, config core_config.Reader, serviceRepo api.ServiceRepository, serviceKeyRepo api.ServiceKeyRepository) (cmd CreateServiceKey) { | ||
return CreateServiceKey{ | ||
ui: ui, | ||
config: config, | ||
serviceRepo: serviceRepo, | ||
serviceKeyRepo: serviceKeyRepo, | ||
} | ||
} | ||
|
||
func (cmd CreateServiceKey) Metadata() command_metadata.CommandMetadata { | ||
return command_metadata.CommandMetadata{ | ||
Name: "create-service-key", | ||
ShortName: "csk", | ||
Description: T("Create key for a service instance"), | ||
Usage: T(`CF_NAME create-service-key SERVICE_INSTANCE SERVICE_KEY | ||
EXAMPLE: | ||
CF_NAME create-service-key mydb mykey`), | ||
} | ||
} | ||
|
||
func (cmd CreateServiceKey) GetRequirements(requirementsFactory requirements.Factory, c *cli.Context) (reqs []requirements.Requirement, err error) { | ||
if len(c.Args()) != 2 { | ||
cmd.ui.FailWithUsage(c) | ||
} | ||
|
||
loginRequirement := requirementsFactory.NewLoginRequirement() | ||
serviceInstanceRequirement := requirementsFactory.NewServiceInstanceRequirement(c.Args()[0]) | ||
targetSpaceRequirement := requirementsFactory.NewTargetedSpaceRequirement() | ||
|
||
reqs = []requirements.Requirement{loginRequirement, serviceInstanceRequirement, targetSpaceRequirement} | ||
|
||
return reqs, nil | ||
} | ||
|
||
func (cmd CreateServiceKey) Run(c *cli.Context) { | ||
serviceInstanceName := c.Args()[0] | ||
serviceKeyName := c.Args()[1] | ||
|
||
cmd.ui.Say(T("Creating service key {{.ServiceKeyName}} for service instance {{.ServiceInstanceName}} as {{.CurrentUser}}...", | ||
map[string]interface{}{ | ||
"ServiceInstanceName": terminal.EntityNameColor(serviceInstanceName), | ||
"ServiceKeyName": terminal.EntityNameColor(serviceKeyName), | ||
"CurrentUser": terminal.EntityNameColor(cmd.config.Username()), | ||
})) | ||
|
||
serviceInstance, err := cmd.serviceRepo.FindInstanceByName(serviceInstanceName) | ||
if err != nil { | ||
cmd.ui.Failed(err.Error()) | ||
return | ||
} | ||
|
||
err = cmd.serviceKeyRepo.CreateServiceKey(serviceInstance.Guid, serviceKeyName) | ||
if err != nil { | ||
cmd.ui.Failed(err.Error()) | ||
return | ||
} | ||
|
||
switch err.(type) { | ||
case nil: | ||
cmd.ui.Ok() | ||
case *errors.ModelAlreadyExistsError: | ||
cmd.ui.Ok() | ||
cmd.ui.Warn(err.Error()) | ||
default: | ||
cmd.ui.Failed(err.Error()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package servicekey_test | ||
|
||
import ( | ||
"github.com/cloudfoundry/cli/cf/configuration/core_config" | ||
"github.com/cloudfoundry/cli/cf/errors" | ||
"github.com/cloudfoundry/cli/cf/models" | ||
"github.com/cloudfoundry/cli/generic" | ||
|
||
testapi "github.com/cloudfoundry/cli/cf/api/fakes" | ||
testcmd "github.com/cloudfoundry/cli/testhelpers/commands" | ||
testconfig "github.com/cloudfoundry/cli/testhelpers/configuration" | ||
testreq "github.com/cloudfoundry/cli/testhelpers/requirements" | ||
testterm "github.com/cloudfoundry/cli/testhelpers/terminal" | ||
|
||
. "github.com/cloudfoundry/cli/cf/commands/servicekey" | ||
. "github.com/cloudfoundry/cli/testhelpers/matchers" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
var _ = Describe("create-service-key command", func() { | ||
var ( | ||
ui *testterm.FakeUI | ||
config core_config.Repository | ||
cmd CreateServiceKey | ||
requirementsFactory *testreq.FakeReqFactory | ||
serviceRepo *testapi.FakeServiceRepo | ||
serviceKeyRepo *testapi.FakeServiceKeyRepo | ||
) | ||
|
||
BeforeEach(func() { | ||
ui = &testterm.FakeUI{} | ||
config = testconfig.NewRepositoryWithDefaults() | ||
serviceRepo = &testapi.FakeServiceRepo{} | ||
serviceInstance := models.ServiceInstance{} | ||
serviceInstance.Guid = "fake-instance-guid" | ||
serviceRepo.FindInstanceByNameMap = generic.NewMap() | ||
serviceRepo.FindInstanceByNameMap.Set("fake-service-instance", serviceInstance) | ||
serviceKeyRepo = testapi.NewFakeServiceKeyRepo() | ||
cmd = NewCreateServiceKey(ui, config, serviceRepo, serviceKeyRepo) | ||
requirementsFactory = &testreq.FakeReqFactory{LoginSuccess: true, TargetedSpaceSuccess: true, ServiceInstanceNotFound: false} | ||
}) | ||
|
||
var callCreateService = func(args []string) bool { | ||
return testcmd.RunCommand(cmd, args, requirementsFactory) | ||
} | ||
|
||
Describe("requirements", func() { | ||
It("fails when not logged in", func() { | ||
requirementsFactory = &testreq.FakeReqFactory{LoginSuccess: false} | ||
Expect(callCreateService([]string{"fake-service-instance", "fake-service-key"})).To(BeFalse()) | ||
}) | ||
|
||
It("requires two arguments to run", func() { | ||
Expect(callCreateService([]string{})).To(BeFalse()) | ||
Expect(callCreateService([]string{"fake-arg-one"})).To(BeFalse()) | ||
Expect(callCreateService([]string{"fake-arg-one", "fake-arg-two", "fake-arg-three"})).To(BeFalse()) | ||
}) | ||
|
||
It("fails when service instance is not found", func() { | ||
requirementsFactory = &testreq.FakeReqFactory{LoginSuccess: true, ServiceInstanceNotFound: true} | ||
Expect(callCreateService([]string{"non-exist-service-instance", "fake-service-key"})).To(BeFalse()) | ||
}) | ||
|
||
It("fails when space is not targetted", func() { | ||
requirementsFactory = &testreq.FakeReqFactory{LoginSuccess: true, TargetedSpaceSuccess: false} | ||
Expect(callCreateService([]string{"non-exist-service-instance", "fake-service-key"})).To(BeFalse()) | ||
}) | ||
}) | ||
|
||
Describe("requiremnts are satisfied", func() { | ||
It("create service key successfully", func() { | ||
callCreateService([]string{"fake-service-instance", "fake-service-key"}) | ||
|
||
Expect(ui.Outputs).To(ContainSubstrings( | ||
[]string{"Creating service key", "fake-service-key", "for service instance", "fake-service-instance", "as", "my-user"}, | ||
[]string{"OK"}, | ||
)) | ||
Expect(serviceKeyRepo.CreateServiceKeyArgs.ServiceInstanceId).To(Equal("fake-instance-guid")) | ||
Expect(serviceKeyRepo.CreateServiceKeyArgs.ServiceKeyName).To(Equal("fake-service-key")) | ||
}) | ||
|
||
It("create service key failed when the service key already exists", func() { | ||
serviceKeyRepo.CreateServiceKeyError = errors.NewModelAlreadyExistsError("ServiceKey", "exist-service-key") | ||
callCreateService([]string{"fake-service-instance", "exist-service-key"}) | ||
|
||
Expect(ui.Outputs).To(ContainSubstrings( | ||
[]string{"Creating service key", "exist-service-key", "for service instance", "fake-service-instance", "as", "my-user"}, | ||
[]string{"FAILED"}, | ||
[]string{"ServiceKey exist-service-key already exists"})) | ||
}) | ||
|
||
It("create service key failed when the service is unbindable", func() { | ||
serviceKeyRepo.CreateServiceKeyError = errors.NewUnbindableServiceError() | ||
callCreateService([]string{"fake-service-instance", "exist-service-key"}) | ||
|
||
Expect(ui.Outputs).To(ContainSubstrings( | ||
[]string{"Creating service key", "exist-service-key", "for service instance", "fake-service-instance", "as", "my-user"}, | ||
[]string{"FAILED"}, | ||
[]string{"This service doesn't support creation of keys."})) | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.