Skip to content
This repository was archived by the owner on Jul 28, 2020. It is now read-only.

Commit e5874a3

Browse files
committed
Deploy a service after cf dev is started
Add a `deploy-service` command that allow the user to deploy a new white listed service to the current deployment. CF Dev need to be started in order to execute this command [#161153365]
1 parent 3a9d09e commit e5874a3

File tree

9 files changed

+424
-4
lines changed

9 files changed

+424
-4
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package deploy_service
2+
3+
import (
4+
"code.cloudfoundry.org/cfdev/config"
5+
e "code.cloudfoundry.org/cfdev/errors"
6+
"code.cloudfoundry.org/cfdev/metadata"
7+
"code.cloudfoundry.org/cfdev/provision"
8+
"fmt"
9+
"github.com/pkg/errors"
10+
"github.com/spf13/cobra"
11+
"io"
12+
"os"
13+
"path/filepath"
14+
)
15+
16+
//go:generate mockgen -package mocks -destination mocks/ui.go code.cloudfoundry.org/cfdev/cmd/deploy-service UI
17+
type UI interface {
18+
Say(message string, args ...interface{})
19+
Writer() io.Writer
20+
}
21+
22+
//go:generate mockgen -package mocks -destination mocks/metadata_reader.go code.cloudfoundry.org/cfdev/cmd/deploy-service MetaDataReader
23+
type MetaDataReader interface {
24+
Read(tarballPath string) (metadata.Metadata, error)
25+
}
26+
27+
//go:generate mockgen -package mocks -destination mocks/provisioner.go code.cloudfoundry.org/cfdev/cmd/deploy-service Provisioner
28+
type Provisioner interface {
29+
Ping() error
30+
DeployServices(provision.UI, []provision.Service) error
31+
GetWhiteListedService(string, []provision.Service) (*provision.Service, error)
32+
}
33+
34+
const compatibilityVersion = "v3"
35+
36+
type DeployService struct {
37+
Exit chan struct{}
38+
UI UI
39+
Provisioner Provisioner
40+
MetaDataReader MetaDataReader
41+
Config config.Config
42+
}
43+
44+
type Args struct {
45+
Service string
46+
}
47+
48+
func (c *DeployService) Cmd() *cobra.Command {
49+
cmd := &cobra.Command{
50+
Use: "deploy-service",
51+
RunE: c.RunE,
52+
Short: "Deploy a new service",
53+
Long: "Command deploy a new service provided as a parameter",
54+
}
55+
56+
return cmd
57+
}
58+
59+
func (c *DeployService) RunE(cmd *cobra.Command, args []string) error {
60+
go func() {
61+
<-c.Exit
62+
os.Exit(128)
63+
}()
64+
65+
if len(args) != 1 {
66+
return errors.New("A service name need to be passed as a argument")
67+
}
68+
69+
return c.Execute(Args{
70+
Service: args[0],
71+
})
72+
}
73+
74+
func (c *DeployService) Execute(args Args) error {
75+
metadataConfig, err := c.MetaDataReader.Read(filepath.Join(c.Config.CacheDir, "metadata.yml"))
76+
if err != nil {
77+
return e.SafeWrap(err, fmt.Sprintf("something went wrong while reading the assets. Please execute 'cf dev start'"))
78+
}
79+
80+
if metadataConfig.Version != compatibilityVersion {
81+
return fmt.Errorf("asset version is incompatible with the current version of the plugin. Please execute 'cf dev start'")
82+
}
83+
84+
if c.Provisioner.Ping() != nil {
85+
return fmt.Errorf("cf dev is not running. Please execute 'cf dev start'")
86+
}
87+
88+
var service *provision.Service
89+
service, err = c.Provisioner.GetWhiteListedService(args.Service, metadataConfig.Services)
90+
if err != nil {
91+
return e.SafeWrap(err, "Failed to whitelist service")
92+
}
93+
94+
if err := c.Provisioner.DeployServices(c.UI, []provision.Service{*service}); err != nil {
95+
return e.SafeWrap(err, "Failed to deploy services")
96+
}
97+
98+
return nil
99+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package deploy_service
2+
3+
import (
4+
"testing"
5+
6+
. "github.com/onsi/ginkgo"
7+
. "github.com/onsi/gomega"
8+
)
9+
10+
func TestDeployService(t *testing.T) {
11+
RegisterFailHandler(Fail)
12+
RunSpecs(t, "Cmd Deploy Service Suite")
13+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package deploy_service_test
2+
3+
import (
4+
"code.cloudfoundry.org/cfdev/cmd/deploy-service"
5+
"code.cloudfoundry.org/cfdev/cmd/deploy-service/mocks"
6+
"code.cloudfoundry.org/cfdev/config"
7+
"code.cloudfoundry.org/cfdev/metadata"
8+
"code.cloudfoundry.org/cfdev/provision"
9+
"errors"
10+
"github.com/golang/mock/gomock"
11+
. "github.com/onsi/ginkgo"
12+
. "github.com/onsi/gomega"
13+
"path/filepath"
14+
)
15+
16+
var _ = Describe("DeployService", func() {
17+
var (
18+
mockController *gomock.Controller
19+
mockMetadataReader *mocks.MockMetaDataReader
20+
mockProvisioner *mocks.MockProvisioner
21+
mockUI *mocks.MockUI
22+
cmd *deploy_service.DeployService
23+
)
24+
BeforeEach(func() {
25+
mockController = gomock.NewController(GinkgoT())
26+
mockUI = mocks.NewMockUI(mockController)
27+
mockMetadataReader = mocks.NewMockMetaDataReader(mockController)
28+
mockProvisioner = mocks.NewMockProvisioner(mockController)
29+
30+
cmd = &deploy_service.DeployService{
31+
UI: mockUI,
32+
MetaDataReader: mockMetadataReader,
33+
Provisioner: mockProvisioner,
34+
Config: config.Config{
35+
CacheDir: "some-cache-dir",
36+
},
37+
}
38+
})
39+
40+
AfterEach(func() {})
41+
42+
Describe("happy path", func() {
43+
It("deploys a new service", func() {
44+
service := provision.Service{
45+
Name: "some-service",
46+
}
47+
mockMetadataReader.EXPECT().Read(filepath.Join("some-cache-dir", "metadata.yml")).Return(metadata.Metadata{
48+
Version: "v3",
49+
Services: []provision.Service{service},
50+
}, nil)
51+
mockProvisioner.EXPECT().Ping().Return(nil)
52+
mockProvisioner.EXPECT().GetWhiteListedService("some-service", []provision.Service{service}).Return(&service, nil)
53+
54+
mockProvisioner.EXPECT().DeployServices(mockUI, []provision.Service{service}).Return(nil)
55+
56+
err := cmd.Execute(deploy_service.Args{
57+
Service: "some-service",
58+
})
59+
Expect(err).NotTo(HaveOccurred())
60+
})
61+
})
62+
63+
Describe("When cf dev is not running", func() {
64+
It("returns an error", func() {
65+
service := provision.Service{
66+
Name: "some-service",
67+
}
68+
mockMetadataReader.EXPECT().Read(filepath.Join("some-cache-dir", "metadata.yml")).Return(metadata.Metadata{
69+
Version: "v3",
70+
Services: []provision.Service{service},
71+
}, nil)
72+
mockProvisioner.EXPECT().Ping().Return(errors.New("some issue happened"))
73+
74+
err := cmd.Execute(deploy_service.Args{
75+
Service: "some-service",
76+
})
77+
Expect(err).To(HaveOccurred())
78+
Expect(err.Error()).To(ContainSubstring("cf dev is not running"))
79+
})
80+
})
81+
82+
Describe("When service is not whitelisted", func() {
83+
It("returns an error", func() {
84+
service := provision.Service{
85+
Name: "some-service",
86+
}
87+
mockMetadataReader.EXPECT().Read(filepath.Join("some-cache-dir", "metadata.yml")).Return(metadata.Metadata{
88+
Version: "v3",
89+
Services: []provision.Service{service},
90+
}, nil)
91+
mockProvisioner.EXPECT().Ping().Return(nil)
92+
mockProvisioner.EXPECT().GetWhiteListedService(
93+
"some-service",
94+
[]provision.Service{service}).Return(&service, errors.New("unknown service"))
95+
96+
97+
err := cmd.Execute(deploy_service.Args{
98+
Service: "some-service",
99+
})
100+
Expect(err).To(HaveOccurred())
101+
Expect(err.Error()).To(ContainSubstring("Failed to whitelist service"))
102+
})
103+
})
104+
})

cmd/deploy-service/mocks/metadata_reader.go

Lines changed: 47 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/deploy-service/mocks/provisioner.go

Lines changed: 71 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)