Skip to content

Commit

Permalink
Add 'create-service-key' command in cli, story number #{87057732}
Browse files Browse the repository at this point in the history
Add a new command named "create-service-key" to create a service key
for a specified service instance.

Signed-off-by: zhang-hua <zhuadl@cn.ibm.com>
  • Loading branch information
xingzhou authored and zhang-hua committed Mar 20, 2015
1 parent 7cae331 commit b2406bc
Show file tree
Hide file tree
Showing 20 changed files with 773 additions and 10 deletions.
25 changes: 25 additions & 0 deletions cf/api/fakes/fake_service_key_repo.go
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
}
6 changes: 6 additions & 0 deletions cf/api/repository_locator.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type RepositoryLocator struct {
routeRepo CloudControllerRouteRepository
stackRepo stacks.CloudControllerStackRepository
serviceRepo CloudControllerServiceRepository
serviceKeyRepo CloudControllerServiceKeyRepository
serviceBindingRepo CloudControllerServiceBindingRepository
serviceSummaryRepo CloudControllerServiceSummaryRepository
userRepo CloudControllerUserRepository
Expand Down Expand Up @@ -111,6 +112,7 @@ func NewRepositoryLocator(config core_config.ReadWriter, gatewaysByName map[stri
loc.routeRepo = NewCloudControllerRouteRepository(config, cloudControllerGateway)
loc.stackRepo = stacks.NewCloudControllerStackRepository(config, cloudControllerGateway)
loc.serviceRepo = NewCloudControllerServiceRepository(config, cloudControllerGateway)
loc.serviceKeyRepo = NewCloudControllerServiceKeyRepository(config, cloudControllerGateway)
loc.serviceBindingRepo = NewCloudControllerServiceBindingRepository(config, cloudControllerGateway)
loc.serviceBrokerRepo = NewCloudControllerServiceBrokerRepository(config, cloudControllerGateway)
loc.servicePlanRepo = NewCloudControllerServicePlanRepository(config, cloudControllerGateway)
Expand Down Expand Up @@ -196,6 +198,10 @@ func (locator RepositoryLocator) GetServiceRepository() ServiceRepository {
return locator.serviceRepo
}

func (locator RepositoryLocator) GetServiceKeyRepository() ServiceKeyRepository {
return locator.serviceKeyRepo
}

func (locator RepositoryLocator) GetServiceBindingRepository() ServiceBindingRepository {
return locator.serviceBindingRepo
}
Expand Down
39 changes: 39 additions & 0 deletions cf/api/service_keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
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)
}

return nil
}
78 changes: 78 additions & 0 deletions cf/api/service_keys_test.go
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()
})
})
2 changes: 2 additions & 0 deletions cf/app/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ func newAppPresenter(app *cli.App) (presenter appPresenter) {
presentCommand("update-service"),
presentCommand("delete-service"),
presentCommand("rename-service"),
}, {
presentCommand("create-service-key"),
}, {
presentCommand("bind-service"),
presentCommand("unbind-service"),
Expand Down
2 changes: 2 additions & 0 deletions cf/command_factory/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/cloudfoundry/cli/cf/commands/serviceaccess"
"github.com/cloudfoundry/cli/cf/commands/serviceauthtoken"
"github.com/cloudfoundry/cli/cf/commands/servicebroker"
"github.com/cloudfoundry/cli/cf/commands/servicekey"
"github.com/cloudfoundry/cli/cf/commands/space"
"github.com/cloudfoundry/cli/cf/commands/spacequota"
"github.com/cloudfoundry/cli/cf/commands/user"
Expand Down Expand Up @@ -100,6 +101,7 @@ func NewFactory(ui terminal.UI, config core_config.ReadWriter, manifestRepo mani

factory.cmdsByName["create-service-auth-token"] = serviceauthtoken.NewCreateServiceAuthToken(ui, config, repoLocator.GetServiceAuthTokenRepository())
factory.cmdsByName["create-service-broker"] = servicebroker.NewCreateServiceBroker(ui, config, repoLocator.GetServiceBrokerRepository())
factory.cmdsByName["create-service-key"] = servicekey.NewCreateServiceKey(ui, config, repoLocator.GetServiceRepository(), repoLocator.GetServiceKeyRepository())
factory.cmdsByName["create-user"] = user.NewCreateUser(ui, config, repoLocator.GetUserRepository())
factory.cmdsByName["create-user-provided-service"] = service.NewCreateUserProvidedService(ui, config, repoLocator.GetUserProvidedServiceInstanceRepository())
factory.cmdsByName["curl"] = commands.NewCurl(ui, config, repoLocator.GetCurlRepository())
Expand Down
89 changes: 89 additions & 0 deletions cf/commands/servicekey/create_service_key.go
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())
}
}
94 changes: 94 additions & 0 deletions cf/commands/servicekey/create_service_key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
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"}))
})
})
})
Loading

0 comments on commit b2406bc

Please sign in to comment.