From ea007623cff8cfe88e8c3f9d8c3f8ccda8d7f138 Mon Sep 17 00:00:00 2001 From: Nima Kaviani Date: Tue, 20 Nov 2018 23:43:05 -0800 Subject: [PATCH 1/5] add service type to blockhead services - read the type of service from the TAGS description of the service config. `ethereum` and `fabric` are the two types of service known to the service broker at the moment. --- cmd/broker/main_suite_test.go | 4 +- cmd/broker/main_test.go | 333 +++++++++++++----- pkg/broker/broker.go | 7 +- pkg/broker/broker_test.go | 6 +- .../bad_services/bad_service_config.json | 18 + .../assets/services/service_config.json | 2 +- .../assets/services/service_config2.json | 2 +- pkg/config/config.go | 44 +++ pkg/config/config_test.go | 18 +- pkg/deployer/deployer.go | 16 +- pkg/deployer/deployer_test.go | 8 +- pkg/fakes/fake_deployer.go | 18 +- pusher.js | 5 +- services/eth.json | 1 + services/fabric.json | 20 ++ 15 files changed, 381 insertions(+), 121 deletions(-) create mode 100644 pkg/config/assets/bad_services/bad_service_config.json create mode 100644 services/fabric.json diff --git a/cmd/broker/main_suite_test.go b/cmd/broker/main_suite_test.go index 0c8c2ca2..f6623d73 100644 --- a/cmd/broker/main_suite_test.go +++ b/cmd/broker/main_suite_test.go @@ -22,8 +22,8 @@ import ( const ( CLI_FLAGS_VERSION = "2.0.7" - WEB3_VERSION = "1.0.0-beta.36" - SOLC_VERSION = "0.4.25" + WEB3_VERSION = "1.0.0-beta.34" + SOLC_VERSION = "0.4.22" ) var ( diff --git a/cmd/broker/main_test.go b/cmd/broker/main_test.go index c0c0d978..4e581c26 100644 --- a/cmd/broker/main_test.go +++ b/cmd/broker/main_test.go @@ -83,11 +83,11 @@ var _ = Describe("Blockhead", func() { }) Context("Service", func() { - var expectedService brokerapi.Service + var expectedETHService, expectedFabricService brokerapi.Service BeforeEach(func() { True := true - expectedService = brokerapi.Service{ + expectedETHService = brokerapi.Service{ ID: "not-checked-in-service-matcher", Name: "eth", Description: "Ethereum Geth Node", @@ -105,25 +105,45 @@ var _ = Describe("Blockhead", func() { }, }, } + + expectedFabricService = brokerapi.Service{ + ID: "not-checked", + Name: "fab3", + Description: "Hyperledger Fabric Proxy", + Bindable: true, + Tags: []string{"fabric", "proxy", "evm"}, + Metadata: &brokerapi.ServiceMetadata{ + DisplayName: "Fabric Proxy 0.1", + }, + Plans: []brokerapi.ServicePlan{ + brokerapi.ServicePlan{ + ID: "not-checked", + Name: "free", + Description: "Free Trial", + Free: &True, + }, + }, + } }) Context("with an existing service", func() { + var ( + serviceId, planId string + cli *dockerclient.Client + ) + It("should successfully return service catalog", func() { resp := requestCatalog(client) Expect(resp.StatusCode).To(Equal(http.StatusOK)) catalog := parseCatalogResponse(resp) Expect(catalog.Services).To(ConsistOf( - utils.EquivalentBrokerAPIService(expectedService)), - ) + utils.EquivalentBrokerAPIService(expectedETHService), + utils.EquivalentBrokerAPIService(expectedFabricService), + )) }) - Context("when provisioning", func() { - var ( - serviceId, planId string - cli *dockerclient.Client - ) - + Context("for ethereum service", func() { BeforeEach(func() { cli, err = dockerclient.NewEnvClient() Expect(err).NotTo(HaveOccurred()) @@ -132,51 +152,126 @@ var _ = Describe("Blockhead", func() { Expect(resp.StatusCode).To(Equal(http.StatusOK)) catalog := parseCatalogResponse(resp) - service := catalog.Services[0] + + var service *brokerapi.Service + for _, s := range catalog.Services { + if contains(s.Tags, "eth") { + service = &s + break + } + } + Expect(service).NotTo(BeNil()) Expect(service.Plans).To(HaveLen(1)) plan := service.Plans[0] serviceId = service.ID planId = plan.ID }) - AfterEach(func() { - for _, containerId := range containers { - err = cli.ContainerRemove(context.Background(), containerId, types.ContainerRemoveOptions{Force: true}) + Context("when provisioning", func() { + AfterEach(func() { + for _, containerId := range containers { + err = cli.ContainerRemove(context.Background(), containerId, types.ContainerRemoveOptions{Force: true}) + Expect(err).NotTo(HaveOccurred()) + } + }) + + It("should successfully provision the service", func() { + instanceId := newContainerId() + resp := requestProvision(client, serviceId, planId, instanceId) + Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + + info, err := cli.ContainerInspect(context.Background(), instanceId) Expect(err).NotTo(HaveOccurred()) - } + Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("8545/tcp"))) + }) + + Context("with an existing node", func() { + BeforeEach(func() { + existingInstanceId := newContainerId() + resp := requestProvision(client, serviceId, planId, existingInstanceId) + Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + }) + + It("successfully launches a second node", func() { + instanceId := newContainerId() + resp := requestProvision(client, serviceId, planId, instanceId) + Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + }) + }) }) - It("should successfully provision the service", func() { - instanceId := newContainerId() - resp := requestProvision(client, serviceId, planId, instanceId) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + Context("when deprovisioning", func() { + var ( + instanceId string + ) - info, err := cli.ContainerInspect(context.Background(), instanceId) - Expect(err).NotTo(HaveOccurred()) - Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("8545/tcp"))) + BeforeEach(func() { + instanceId = newContainerId() + resp := requestProvision(client, serviceId, planId, instanceId) + Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + + info, err := cli.ContainerInspect(context.Background(), instanceId) + Expect(err).NotTo(HaveOccurred()) + Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("8545/tcp"))) + }) + + AfterEach(func() { + for _, containerId := range containers { + err = cli.ContainerRemove(context.Background(), containerId, types.ContainerRemoveOptions{Force: true}) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("No such container")) + } + }) + + It("should successfully deprovision the service", func() { + resp := requestDeprovision(client, serviceId, planId, instanceId) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + }) }) - Context("with an existing node", func() { + Context("when binding", func() { + var ( + instanceId string + ) + BeforeEach(func() { - existingInstanceId := newContainerId() - resp := requestProvision(client, serviceId, planId, existingInstanceId) + instanceId = newContainerId() + resp := requestProvision(client, serviceId, planId, instanceId) Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + + info, err := cli.ContainerInspect(context.Background(), instanceId) + Expect(err).NotTo(HaveOccurred()) + Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("8545/tcp"))) }) - It("successfully launches a second node", func() { - instanceId := newContainerId() - resp := requestProvision(client, serviceId, planId, instanceId) + AfterEach(func() { + for _, containerId := range containers { + err = cli.ContainerRemove(context.Background(), containerId, types.ContainerRemoveOptions{Force: true}) + Expect(err).NotTo(HaveOccurred()) + } + }) + + It("should successfully return node information", func() { + bindingId := uuid.New() + resp := requestBind(client, serviceId, planId, instanceId, bindingId) Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + + bindingResults := brokerapi.Binding{} + body, err := ioutil.ReadAll(resp.Body) + Expect(err).NotTo(HaveOccurred()) + json.Unmarshal(body, &bindingResults) + creds := bindingResults.Credentials.(map[string]interface{}) + containerInfo := creds["ContainerInfo"].(map[string]interface{}) + Expect(containerInfo["Bindings"]).To(HaveKey("8545")) + nodeInfo := creds["NodeInfo"].(map[string]interface{}) + Expect(nodeInfo["Account"]).NotTo(Equal("")) + Expect(nodeInfo["ContractAddress"]).NotTo(Equal("")) }) }) }) - Context("when deprovisioning", func() { - var ( - instanceId, serviceId, planId string - cli *dockerclient.Client - ) - + FContext("for fabric service", func() { BeforeEach(func() { cli, err = dockerclient.NewEnvClient() Expect(err).NotTo(HaveOccurred()) @@ -185,87 +280,124 @@ var _ = Describe("Blockhead", func() { Expect(resp.StatusCode).To(Equal(http.StatusOK)) catalog := parseCatalogResponse(resp) - service := catalog.Services[0] + + var service *brokerapi.Service + for _, s := range catalog.Services { + if contains(s.Tags, "fabric") { + service = &s + break + } + } + Expect(service).NotTo(BeNil()) Expect(service.Plans).To(HaveLen(1)) plan := service.Plans[0] serviceId = service.ID planId = plan.ID + }) - instanceId = newContainerId() - resp = requestProvision(client, serviceId, planId, instanceId) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + Context("when provisioning", func() { + AfterEach(func() { + for _, containerId := range containers { + err = cli.ContainerRemove(context.Background(), containerId, types.ContainerRemoveOptions{Force: true}) + Expect(err).NotTo(HaveOccurred()) + } + }) - info, err := cli.ContainerInspect(context.Background(), instanceId) - Expect(err).NotTo(HaveOccurred()) - Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("8545/tcp"))) - }) + It("should successfully provision the service", func() { + instanceId := newContainerId() + resp := requestProvision(client, serviceId, planId, instanceId) + Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - AfterEach(func() { - for _, containerId := range containers { - err = cli.ContainerRemove(context.Background(), containerId, types.ContainerRemoveOptions{Force: true}) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("No such container")) - } - }) + info, err := cli.ContainerInspect(context.Background(), instanceId) + Expect(err).NotTo(HaveOccurred()) + Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("5000/tcp"))) + }) - It("should successfully deprovision the service", func() { - resp := requestDeprovision(client, serviceId, planId, instanceId) - Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Context("with an existing node", func() { + BeforeEach(func() { + existingInstanceId := newContainerId() + resp := requestProvision(client, serviceId, planId, existingInstanceId) + Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + }) + + It("successfully launches a second node", func() { + instanceId := newContainerId() + resp := requestProvision(client, serviceId, planId, instanceId) + Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + }) + }) }) - }) - Context("when binding", func() { - var ( - instanceId, serviceId, planId string - cli *dockerclient.Client - ) + Context("when deprovisioning", func() { + var ( + instanceId string + cli *dockerclient.Client + ) - BeforeEach(func() { - cli, err = dockerclient.NewEnvClient() - Expect(err).NotTo(HaveOccurred()) - - resp := requestCatalog(client) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) + BeforeEach(func() { + instanceId = newContainerId() + resp := requestProvision(client, serviceId, planId, instanceId) + Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - catalog := parseCatalogResponse(resp) - service := catalog.Services[0] - Expect(service.Plans).To(HaveLen(1)) - plan := service.Plans[0] - serviceId = service.ID - planId = plan.ID + info, err := cli.ContainerInspect(context.Background(), instanceId) + Expect(err).NotTo(HaveOccurred()) + Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("5000/tcp"))) + }) - instanceId = newContainerId() - resp = requestProvision(client, serviceId, planId, instanceId) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + AfterEach(func() { + for _, containerId := range containers { + err = cli.ContainerRemove(context.Background(), containerId, types.ContainerRemoveOptions{Force: true}) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("No such container")) + } + }) - info, err := cli.ContainerInspect(context.Background(), instanceId) - Expect(err).NotTo(HaveOccurred()) - Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("8545/tcp"))) + It("should successfully deprovision the service", func() { + resp := requestDeprovision(client, serviceId, planId, instanceId) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + }) }) - AfterEach(func() { - for _, containerId := range containers { - err = cli.ContainerRemove(context.Background(), containerId, types.ContainerRemoveOptions{Force: true}) + Context("when binding", func() { + var ( + instanceId, serviceId, planId string + cli *dockerclient.Client + ) + + BeforeEach(func() { + instanceId = newContainerId() + resp := requestProvision(client, serviceId, planId, instanceId) + Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + + info, err := cli.ContainerInspect(context.Background(), instanceId) Expect(err).NotTo(HaveOccurred()) - } - }) + Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("5000/tcp"))) + }) - It("should successfully return node information", func() { - bindingId := uuid.New() - resp := requestBind(client, serviceId, planId, instanceId, bindingId) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + AfterEach(func() { + for _, containerId := range containers { + err = cli.ContainerRemove(context.Background(), containerId, types.ContainerRemoveOptions{Force: true}) + Expect(err).NotTo(HaveOccurred()) + } + }) - bindingResults := brokerapi.Binding{} - body, err := ioutil.ReadAll(resp.Body) - Expect(err).NotTo(HaveOccurred()) - json.Unmarshal(body, &bindingResults) - creds := bindingResults.Credentials.(map[string]interface{}) - containerInfo := creds["ContainerInfo"].(map[string]interface{}) - Expect(containerInfo["Bindings"]).To(HaveKey("8545")) - nodeInfo := creds["NodeInfo"].(map[string]interface{}) - Expect(nodeInfo["Account"]).NotTo(Equal("")) - Expect(nodeInfo["ContractAddress"]).NotTo(Equal("")) + It("should successfully return node information", func() { + bindingId := uuid.New() + resp := requestBind(client, serviceId, planId, instanceId, bindingId) + Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + + bindingResults := brokerapi.Binding{} + body, err := ioutil.ReadAll(resp.Body) + Expect(err).NotTo(HaveOccurred()) + json.Unmarshal(body, &bindingResults) + creds := bindingResults.Credentials.(map[string]interface{}) + containerInfo := creds["ContainerInfo"].(map[string]interface{}) + Expect(containerInfo["Bindings"]).To(HaveKey("8545")) + nodeInfo := creds["NodeInfo"].(map[string]interface{}) + Expect(nodeInfo["Account"]).NotTo(Equal("")) + Expect(nodeInfo["ContractAddress"]).NotTo(Equal("")) + }) }) }) }) @@ -387,3 +519,12 @@ func parseCatalogResponse(resp *http.Response) brokerapi.CatalogResponse { Expect(err).NotTo(HaveOccurred()) return catalog } + +func contains(array []string, elem string) bool { + for _, v := range array { + if v == elem { + return true + } + } + return false +} diff --git a/pkg/broker/broker.go b/pkg/broker/broker.go index 163b9357..c8974b68 100644 --- a/pkg/broker/broker.go +++ b/pkg/broker/broker.go @@ -158,7 +158,12 @@ func (b BlockheadBroker) Bind(ctx context.Context, instanceID, bindingID string, return brokerapi.Binding{}, err } - nodeInfo, err := b.deployer.DeployContract(contractInfo, containerInfo, plan.Ports[0]) + deployConfig := deployer.DeployConfig{ + NodePort: plan.Ports[0], + NodeType: service.Type, + } + + nodeInfo, err := b.deployer.DeployContract(contractInfo, containerInfo, deployConfig) if err != nil { return brokerapi.Binding{}, err } diff --git a/pkg/broker/broker_test.go b/pkg/broker/broker_test.go index d35fbe8c..d53477f0 100644 --- a/pkg/broker/broker_test.go +++ b/pkg/broker/broker_test.go @@ -44,6 +44,7 @@ var _ = Describe("Broker", func() { service := config.Service{ Name: "eth", + Type: config.ETHEREUM, Description: "desc", DisplayName: "display-name", Tags: []string{"eth", "geth"}, @@ -389,10 +390,11 @@ var _ = Describe("Broker", func() { // testing the args for call inside the stub since we delete the // contract file as part of the cleanup and after binding is done - fakeDeployer.DeployContractStub = func(contractInfo *deployer.ContractInfo, containerInfo *containermanager.ContainerInfo, nodePort string) (*deployer.NodeInfo, error) { + fakeDeployer.DeployContractStub = func(contractInfo *deployer.ContractInfo, containerInfo *containermanager.ContainerInfo, deployConfig deployer.DeployConfig) (*deployer.NodeInfo, error) { Expect(contractInfo.ContractPath).To(BeAnExistingFile()) Expect(containerInfo).To(Equal(expectedContainerInfo)) - Expect(nodePort).To(Equal("1234")) + Expect(deployConfig.NodePort).To(Equal("1234")) + Expect(deployConfig.NodeType).To(Equal(config.ETHEREUM)) return nil, nil } }) diff --git a/pkg/config/assets/bad_services/bad_service_config.json b/pkg/config/assets/bad_services/bad_service_config.json new file mode 100644 index 00000000..c0cccec8 --- /dev/null +++ b/pkg/config/assets/bad_services/bad_service_config.json @@ -0,0 +1,18 @@ +{ + "name": "name-2", + "description": "desc", + "tags": [ + "random" + ], + "display_name": "display-name", + "plans": [ + { + "name": "plan-name-2", + "image": "image", + "ports": [ + "1234" + ], + "description": "plan-desc" + } + ] +} diff --git a/pkg/config/assets/services/service_config.json b/pkg/config/assets/services/service_config.json index d82ec3c4..28e7bdb0 100644 --- a/pkg/config/assets/services/service_config.json +++ b/pkg/config/assets/services/service_config.json @@ -2,7 +2,7 @@ "name": "name", "description": "desc", "tags": [ - "eth", + "ethereum", "geth" ], "display_name": "display-name", diff --git a/pkg/config/assets/services/service_config2.json b/pkg/config/assets/services/service_config2.json index 4ff538ed..a25ecad7 100644 --- a/pkg/config/assets/services/service_config2.json +++ b/pkg/config/assets/services/service_config2.json @@ -2,7 +2,7 @@ "name": "name-2", "description": "desc", "tags": [ - "eth", + "ethereum", "geth" ], "display_name": "display-name", diff --git a/pkg/config/config.go b/pkg/config/config.go index efe735fd..4340780c 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "github.com/pborman/uuid" ) @@ -24,8 +25,37 @@ type Config struct { ExternalAddress string `json:"external_address,omitempty"` } +type ServiceType int + +const ( + ETHEREUM ServiceType = iota + FABRIC +) + +var serviceTypeStr = [...]string{ + ETHEREUM: "ethereum", + FABRIC: "fabric", +} + +func (s ServiceType) String() string { + if ETHEREUM <= s && s <= FABRIC { + return serviceTypeStr[s] + } + return "invalid" +} + +func serviceTypeFromString(s string) (ServiceType, error) { + for k, v := range serviceTypeStr { + if v == strings.ToLower(s) { + return ServiceType(k), nil + } + } + return -1, fmt.Errorf("invalid service type: %s", s) +} + type Service struct { Name string + Type ServiceType Description string DisplayName string Tags []string @@ -41,6 +71,7 @@ type Plan struct { type ServiceDefinition struct { Name string `json:"name"` + Type string `json:"type"` Description string `json:"description"` DisplayName string `json:"display_name"` Tags []string `json:"tags"` @@ -112,8 +143,21 @@ func NewState(configPath string, servicePath string) (*State, error) { return nil, fmt.Errorf("Error parsing service file: %s - %v", serviceFilePath, err.Error()) } + var serviceType *ServiceType + for _, tag := range serviceDef.Tags { + if svc, err := serviceTypeFromString(tag); err == nil { + serviceType = &svc + break + } + } + + if serviceType == nil { + return nil, fmt.Errorf(fmt.Sprintf("Service type not found: %s", serviceFilePath)) + } + service := Service{ Name: serviceDef.Name, + Type: *serviceType, Description: serviceDef.Description, DisplayName: serviceDef.DisplayName, Tags: serviceDef.Tags, diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 46748c86..de52efdb 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -140,7 +140,7 @@ var _ = Describe("Config", func() { Name: "name", Description: "desc", DisplayName: "display-name", - Tags: []string{"eth", "geth"}, + Tags: []string{"ethereum", "geth"}, } expectedPlan := config.Plan{ @@ -172,7 +172,7 @@ var _ = Describe("Config", func() { Name: "name-2", Description: "desc", DisplayName: "display-name", - Tags: []string{"eth", "geth"}, + Tags: []string{"ethereum", "geth"}, } expectedPlan := config.Plan{ @@ -245,6 +245,20 @@ var _ = Describe("Config", func() { )) }) }) + + Context("when there is a service file with wrong type", func() { + BeforeEach(func() { + serviceFilePath = fmt.Sprintf("%s/bad_service_config.json", servicePath) + copy("assets/bad_services/bad_service_config.json", serviceFilePath) + }) + + It("should have one service", func() { + _, err := config.NewState(configPath, servicePath) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("Service type not found")) + Expect(err.Error()).To(ContainSubstring(serviceFilePath)) + }) + }) }) }) }) diff --git a/pkg/deployer/deployer.go b/pkg/deployer/deployer.go index 0bf9d7a7..723116b6 100644 --- a/pkg/deployer/deployer.go +++ b/pkg/deployer/deployer.go @@ -9,6 +9,7 @@ import ( "os/exec" "code.cloudfoundry.org/lager" + "github.com/cloudfoundry-incubator/blockhead/pkg/config" "github.com/cloudfoundry-incubator/blockhead/pkg/containermanager" "github.com/pborman/uuid" ) @@ -27,9 +28,14 @@ type NodeInfo struct { TransactionHash string `json:"transaction_hash"` } +type DeployConfig struct { + NodePort string + NodeType config.ServiceType +} + //go:generate counterfeiter -o ../fakes/fake_deployer.go . Deployer type Deployer interface { - DeployContract(contractInfo *ContractInfo, containerInfo *containermanager.ContainerInfo, nodePort string) (*NodeInfo, error) + DeployContract(contractInfo *ContractInfo, containerInfo *containermanager.ContainerInfo, deployConfig DeployConfig) (*NodeInfo, error) } type ethereumDeployer struct { @@ -44,21 +50,23 @@ func NewEthereumDeployer(logger lager.Logger, deployerPath string) Deployer { } } -func (e ethereumDeployer) DeployContract(contractInfo *ContractInfo, containerInfo *containermanager.ContainerInfo, nodePort string) (*NodeInfo, error) { +func (e ethereumDeployer) DeployContract(contractInfo *ContractInfo, containerInfo *containermanager.ContainerInfo, deployConfig DeployConfig) (*NodeInfo, error) { e.logger.Info("deploy-started") defer e.logger.Info("deploy-finished") // nodePort is the port we want from the blockchain node - portBindings := containerInfo.Bindings[nodePort] + portBindings := containerInfo.Bindings[deployConfig.NodePort] if len(portBindings) <= 0 { - return nil, errors.New(fmt.Sprintf("Port Bindings do not have %s port mapping", nodePort)) + return nil, errors.New(fmt.Sprintf("Port Bindings do not have %s port mapping", deployConfig.NodePort)) } config := struct { Provider string `json:"provider"` + Type string `json:"type"` Password string `json:"password"` Args []string `json:"args"` }{ Provider: fmt.Sprintf("http://%s:%s", containerInfo.InternalAddress, portBindings[0].Port), + Type: deployConfig.NodeType.String(), Password: "", Args: contractInfo.ContractArgs, } diff --git a/pkg/deployer/deployer_test.go b/pkg/deployer/deployer_test.go index 74958b0e..95dcfbac 100644 --- a/pkg/deployer/deployer_test.go +++ b/pkg/deployer/deployer_test.go @@ -4,6 +4,7 @@ import ( "path/filepath" "code.cloudfoundry.org/lager/lagertest" + "github.com/cloudfoundry-incubator/blockhead/pkg/config" "github.com/cloudfoundry-incubator/blockhead/pkg/containermanager" "github.com/cloudfoundry-incubator/blockhead/pkg/deployer" . "github.com/onsi/ginkgo" @@ -41,7 +42,12 @@ var _ = Describe("Deployer", func() { Bindings: portBindings, } - nodeInfo, err := contractDeployer.DeployContract(contractInfo, containerInfo, nodePort) + deployConfig := deployer.DeployConfig{ + NodePort: nodePort, + NodeType: config.ETHEREUM, + } + + nodeInfo, err := contractDeployer.DeployContract(contractInfo, containerInfo, deployConfig) Expect(err).ToNot(HaveOccurred()) expectedNodeInfo := &deployer.NodeInfo{ diff --git a/pkg/fakes/fake_deployer.go b/pkg/fakes/fake_deployer.go index 955b246d..158fef4c 100644 --- a/pkg/fakes/fake_deployer.go +++ b/pkg/fakes/fake_deployer.go @@ -9,12 +9,12 @@ import ( ) type FakeDeployer struct { - DeployContractStub func(contractInfo *deployer.ContractInfo, containerInfo *containermanager.ContainerInfo, nodePort string) (*deployer.NodeInfo, error) + DeployContractStub func(contractInfo *deployer.ContractInfo, containerInfo *containermanager.ContainerInfo, deployConfig deployer.DeployConfig) (*deployer.NodeInfo, error) deployContractMutex sync.RWMutex deployContractArgsForCall []struct { contractInfo *deployer.ContractInfo containerInfo *containermanager.ContainerInfo - nodePort string + deployConfig deployer.DeployConfig } deployContractReturns struct { result1 *deployer.NodeInfo @@ -28,18 +28,18 @@ type FakeDeployer struct { invocationsMutex sync.RWMutex } -func (fake *FakeDeployer) DeployContract(contractInfo *deployer.ContractInfo, containerInfo *containermanager.ContainerInfo, nodePort string) (*deployer.NodeInfo, error) { +func (fake *FakeDeployer) DeployContract(contractInfo *deployer.ContractInfo, containerInfo *containermanager.ContainerInfo, deployConfig deployer.DeployConfig) (*deployer.NodeInfo, error) { fake.deployContractMutex.Lock() ret, specificReturn := fake.deployContractReturnsOnCall[len(fake.deployContractArgsForCall)] fake.deployContractArgsForCall = append(fake.deployContractArgsForCall, struct { contractInfo *deployer.ContractInfo containerInfo *containermanager.ContainerInfo - nodePort string - }{contractInfo, containerInfo, nodePort}) - fake.recordInvocation("DeployContract", []interface{}{contractInfo, containerInfo, nodePort}) + deployConfig deployer.DeployConfig + }{contractInfo, containerInfo, deployConfig}) + fake.recordInvocation("DeployContract", []interface{}{contractInfo, containerInfo, deployConfig}) fake.deployContractMutex.Unlock() if fake.DeployContractStub != nil { - return fake.DeployContractStub(contractInfo, containerInfo, nodePort) + return fake.DeployContractStub(contractInfo, containerInfo, deployConfig) } if specificReturn { return ret.result1, ret.result2 @@ -53,10 +53,10 @@ func (fake *FakeDeployer) DeployContractCallCount() int { return len(fake.deployContractArgsForCall) } -func (fake *FakeDeployer) DeployContractArgsForCall(i int) (*deployer.ContractInfo, *containermanager.ContainerInfo, string) { +func (fake *FakeDeployer) DeployContractArgsForCall(i int) (*deployer.ContractInfo, *containermanager.ContainerInfo, deployer.DeployConfig) { fake.deployContractMutex.RLock() defer fake.deployContractMutex.RUnlock() - return fake.deployContractArgsForCall[i].contractInfo, fake.deployContractArgsForCall[i].containerInfo, fake.deployContractArgsForCall[i].nodePort + return fake.deployContractArgsForCall[i].contractInfo, fake.deployContractArgsForCall[i].containerInfo, fake.deployContractArgsForCall[i].deployConfig } func (fake *FakeDeployer) DeployContractReturns(result1 *deployer.NodeInfo, result2 error) { diff --git a/pusher.js b/pusher.js index 778f4eb0..496f57cc 100644 --- a/pusher.js +++ b/pusher.js @@ -17,6 +17,7 @@ var config = JSON.parse(configContent); const provider = config.provider const password = config.password const contractArgs = config.args +const nodeType = config.type const contractPath = args.contract result = {} @@ -72,11 +73,11 @@ function convertArray(argument){ const constructorArgs = convertArray(contractArgs) function runDeployment(contract){ - web3.eth.personal.getAccounts() + web3.eth.getAccounts() .then(addresses => { address = addresses[0] result["address"] = address - return web3.eth.personal.unlockAccount(address, '') + return (nodeType == "ethereum") ? web3.eth.personal.unlockAccount(address, '') : address }) .then(() => { var contractAbi = contract.interface diff --git a/services/eth.json b/services/eth.json index 9d0587ab..96fe28a4 100644 --- a/services/eth.json +++ b/services/eth.json @@ -1,5 +1,6 @@ { "name": "eth", + "type": "ethereum", "description": "Ethereum Geth Node", "tags": [ "eth", diff --git a/services/fabric.json b/services/fabric.json new file mode 100644 index 00000000..6504c20c --- /dev/null +++ b/services/fabric.json @@ -0,0 +1,20 @@ +{ + "name": "fab3", + "type": "fabric", + "description": "Hyperledger Fabric Proxy", + "tags": [ + "fabric", + "proxy", + "evm" + ], + "display_name": "Fabric Proxy 0.1", + "plans": [ + { + "name": "free", + "free": true, + "image": "nimak/evm-proxy:latest", + "description": "Free Trial", + "ports": ["5000"] + } + ] +} From b1e24cfdb09a4032b84f5f5537099ed1f3d1037f Mon Sep 17 00:00:00 2001 From: Nima Kaviani Date: Sun, 25 Nov 2018 11:23:14 -0800 Subject: [PATCH 2/5] fix integration tests - stop the running broker process after each test - unifies setup for all the integration tests - set package dependencies back to the right versions --- cmd/broker/main_suite_test.go | 4 ++-- cmd/broker/main_test.go | 20 ++++++++++++-------- services/eth.json | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/cmd/broker/main_suite_test.go b/cmd/broker/main_suite_test.go index f6623d73..0c8c2ca2 100644 --- a/cmd/broker/main_suite_test.go +++ b/cmd/broker/main_suite_test.go @@ -22,8 +22,8 @@ import ( const ( CLI_FLAGS_VERSION = "2.0.7" - WEB3_VERSION = "1.0.0-beta.34" - SOLC_VERSION = "0.4.22" + WEB3_VERSION = "1.0.0-beta.36" + SOLC_VERSION = "0.4.25" ) var ( diff --git a/cmd/broker/main_test.go b/cmd/broker/main_test.go index 4e581c26..004b9cd0 100644 --- a/cmd/broker/main_test.go +++ b/cmd/broker/main_test.go @@ -7,6 +7,7 @@ import ( "fmt" "io/ioutil" "net/http" + "os" "os/exec" "github.com/cloudfoundry-incubator/blockhead/pkg/utils" @@ -60,6 +61,7 @@ var _ = Describe("Blockhead", func() { Context("when both args are passed in", func() { var ( client *http.Client + cmd *exec.Cmd ) var newContainerId = func() string { @@ -77,11 +79,15 @@ var _ = Describe("Blockhead", func() { } client = &http.Client{} - cmd := exec.Command(brokerBinPath, args...) + cmd = exec.Command(brokerBinPath, args...) session, err = gexec.Start(cmd, GinkgoWriter, GinkgoWriter) Expect(err).ToNot(HaveOccurred()) }) + AfterEach(func() { + cmd.Process.Signal(os.Kill) + }) + Context("Service", func() { var expectedETHService, expectedFabricService brokerapi.Service @@ -92,7 +98,7 @@ var _ = Describe("Blockhead", func() { Name: "eth", Description: "Ethereum Geth Node", Bindable: true, - Tags: []string{"eth", "geth", "dev"}, + Tags: []string{"ethereum", "geth", "dev"}, Metadata: &brokerapi.ServiceMetadata{ DisplayName: "Geth 1.8", }, @@ -155,7 +161,7 @@ var _ = Describe("Blockhead", func() { var service *brokerapi.Service for _, s := range catalog.Services { - if contains(s.Tags, "eth") { + if contains(s.Tags, "ethereum") { service = &s break } @@ -271,7 +277,7 @@ var _ = Describe("Blockhead", func() { }) }) - FContext("for fabric service", func() { + Context("for fabric service", func() { BeforeEach(func() { cli, err = dockerclient.NewEnvClient() Expect(err).NotTo(HaveOccurred()) @@ -331,7 +337,6 @@ var _ = Describe("Blockhead", func() { Context("when deprovisioning", func() { var ( instanceId string - cli *dockerclient.Client ) BeforeEach(func() { @@ -361,8 +366,7 @@ var _ = Describe("Blockhead", func() { Context("when binding", func() { var ( - instanceId, serviceId, planId string - cli *dockerclient.Client + instanceId string ) BeforeEach(func() { @@ -393,7 +397,7 @@ var _ = Describe("Blockhead", func() { json.Unmarshal(body, &bindingResults) creds := bindingResults.Credentials.(map[string]interface{}) containerInfo := creds["ContainerInfo"].(map[string]interface{}) - Expect(containerInfo["Bindings"]).To(HaveKey("8545")) + Expect(containerInfo["Bindings"]).To(HaveKey("5000")) nodeInfo := creds["NodeInfo"].(map[string]interface{}) Expect(nodeInfo["Account"]).NotTo(Equal("")) Expect(nodeInfo["ContractAddress"]).NotTo(Equal("")) diff --git a/services/eth.json b/services/eth.json index 96fe28a4..961df41b 100644 --- a/services/eth.json +++ b/services/eth.json @@ -3,7 +3,7 @@ "type": "ethereum", "description": "Ethereum Geth Node", "tags": [ - "eth", + "ethereum", "geth", "dev" ], From 4291bb39f7d7c910ef6a571bf86ae179303d00e0 Mon Sep 17 00:00:00 2001 From: Nima Kaviani Date: Sun, 25 Nov 2018 15:09:02 -0800 Subject: [PATCH 3/5] update README --- README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a73ab06e..94bc4daf 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# blockhead +# Blockhead Service Broker [![Build Status](https://travis-ci.org/cloudfoundry-incubator/blockhead.svg?branch=master)](https://travis-ci.org/cloudfoundry-incubator/blockhead) -An OSBAPI-compatible broker written in golang. + BlockHead is a dedicated service broker developed based on the Open Service Broker (OSB) API that allows for the creation and deployment of smart contracts through creation and binding of services. +## Configure and Run Configure the broker, by setting `username` and `password` in `config.json`. - Run the broker by doing: go run ./cmd/broker/main.go config.json services @@ -29,3 +29,15 @@ To do all the make steps: make +# Blockchain Support +Blockhead currently supports two types of Blockchain networks: + +- The Ethereum public blockchain network. +- The permissioned Fabric blockchain network. + +The type of the blockchain network that is offered by Blockhead is determined by setting the `Tag` in the service definition provided to Blockhead. + +For an Ethereum blockchain network, the service definition tags should include `ethereum`. For the Fabric network, the tags in the service definition should include `fabric`. A service definition lacking either of the two tags is considered incompatible with the broker. + +Examples can be found under the `services/` directory in the repository. + From ac73fd2109cfdc7ab22a832235849094fd6d5563 Mon Sep 17 00:00:00 2001 From: Nima Kaviani Date: Sun, 25 Nov 2018 15:47:40 -0800 Subject: [PATCH 4/5] remove unneeded type from the service definitions - log exit code for the deployer when running the pusher fails - also add log line to capture configuratin json that gets passed to pusher.js --- pkg/deployer/deployer.go | 11 ++++++++++- services/eth.json | 1 - services/fabric.json | 1 - 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/pkg/deployer/deployer.go b/pkg/deployer/deployer.go index 723116b6..b64ff0db 100644 --- a/pkg/deployer/deployer.go +++ b/pkg/deployer/deployer.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "os" "os/exec" + "syscall" "code.cloudfoundry.org/lager" "github.com/cloudfoundry-incubator/blockhead/pkg/config" @@ -83,6 +84,8 @@ func (e ethereumDeployer) DeployContract(contractInfo *ContractInfo, containerIn return nil, err } + e.logger.Info("push-configuration", lager.Data{"config": string(configJson)}) + outputFile, err := ioutil.TempFile("", uuid.New()) if err != nil { return nil, err @@ -92,7 +95,13 @@ func (e ethereumDeployer) DeployContract(contractInfo *ContractInfo, containerIn cmd := exec.Command("node", e.deployerPath, "-c", configFile.Name(), "-o", outputFile.Name(), contractInfo.ContractPath) output, err := cmd.CombinedOutput() if err != nil { - e.logger.Error("run-failed", err, lager.Data{"output": string(output)}) + if exiterr, ok := err.(*exec.ExitError); ok { + if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { + e.logger.Error("exitted-with-status", err, lager.Data{"code": status.ExitStatus(), "output": string(output)}) + } + } else { + e.logger.Error("cmd-wait-failed", err, lager.Data{"output": string(output)}) + } return nil, err } diff --git a/services/eth.json b/services/eth.json index 961df41b..81bb4ee9 100644 --- a/services/eth.json +++ b/services/eth.json @@ -1,6 +1,5 @@ { "name": "eth", - "type": "ethereum", "description": "Ethereum Geth Node", "tags": [ "ethereum", diff --git a/services/fabric.json b/services/fabric.json index 6504c20c..463d24ee 100644 --- a/services/fabric.json +++ b/services/fabric.json @@ -1,6 +1,5 @@ { "name": "fab3", - "type": "fabric", "description": "Hyperledger Fabric Proxy", "tags": [ "fabric", From 2f439e6ff90659c85c11e78f15bfbcce6d3acfd2 Mon Sep 17 00:00:00 2001 From: Swetha Repakula Date: Thu, 6 Dec 2018 10:52:35 -0800 Subject: [PATCH 5/5] Provision take in args and channel for fabric services --- cmd/broker/main_suite_test.go | 6 +- cmd/broker/main_test.go | 65 +++++++++++--- pkg/broker/broker.go | 28 +++++++ pkg/broker/broker_test.go | 84 +++++++++++++++++++ pkg/config/config_test.go | 48 ++++++++++- pkg/containermanager/container_manager.go | 1 + pkg/containermanager/docker/docker_manager.go | 1 + .../docker/docker_manager_test.go | 2 + services/fabric.json | 15 +++- 9 files changed, 231 insertions(+), 19 deletions(-) diff --git a/cmd/broker/main_suite_test.go b/cmd/broker/main_suite_test.go index 0c8c2ca2..c4a10767 100644 --- a/cmd/broker/main_suite_test.go +++ b/cmd/broker/main_suite_test.go @@ -56,7 +56,9 @@ var _ = SynchronizedBeforeSuite(func() []byte { // images are unaffected jsonString := `{ "reference":{ - "nimak/geth:latest": true + "nimak/geth:latest": true, + "swayr/fab3-org1": true, + "swayr/fab3-org2": true } } ` @@ -71,7 +73,7 @@ var _ = SynchronizedBeforeSuite(func() []byte { Expect(err).ToNot(HaveOccurred()) if len(images) > 0 { - Expect(images).To(HaveLen(1)) + Expect(len(images)).To(BeNumerically("<", 3)) imageId := images[0].ID removedImages, err := cli.ImageRemove(context.Background(), imageId, types.ImageRemoveOptions{Force: true}) diff --git a/cmd/broker/main_test.go b/cmd/broker/main_test.go index 004b9cd0..ab24fa9d 100644 --- a/cmd/broker/main_test.go +++ b/cmd/broker/main_test.go @@ -124,8 +124,14 @@ var _ = Describe("Blockhead", func() { Plans: []brokerapi.ServicePlan{ brokerapi.ServicePlan{ ID: "not-checked", - Name: "free", - Description: "Free Trial", + Name: "fab3-org1", + Description: "Fab3 org 1", + Free: &True, + }, + brokerapi.ServicePlan{ + ID: "not-checked", + Name: "fab3-org2", + Description: "Fab3 org 2", Free: &True, }, }, @@ -295,7 +301,7 @@ var _ = Describe("Blockhead", func() { } } Expect(service).NotTo(BeNil()) - Expect(service.Plans).To(HaveLen(1)) + Expect(service.Plans).To(HaveLen(2)) plan := service.Plans[0] serviceId = service.ID planId = plan.ID @@ -311,24 +317,24 @@ var _ = Describe("Blockhead", func() { It("should successfully provision the service", func() { instanceId := newContainerId() - resp := requestProvision(client, serviceId, planId, instanceId) + resp := requestFabricProvision(client, serviceId, planId, instanceId) Expect(resp.StatusCode).To(Equal(http.StatusCreated)) info, err := cli.ContainerInspect(context.Background(), instanceId) Expect(err).NotTo(HaveOccurred()) - Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("5000/tcp"))) + Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("8545/tcp"))) }) Context("with an existing node", func() { BeforeEach(func() { existingInstanceId := newContainerId() - resp := requestProvision(client, serviceId, planId, existingInstanceId) + resp := requestFabricProvision(client, serviceId, planId, existingInstanceId) Expect(resp.StatusCode).To(Equal(http.StatusCreated)) }) It("successfully launches a second node", func() { instanceId := newContainerId() - resp := requestProvision(client, serviceId, planId, instanceId) + resp := requestFabricProvision(client, serviceId, planId, instanceId) Expect(resp.StatusCode).To(Equal(http.StatusCreated)) }) }) @@ -341,12 +347,12 @@ var _ = Describe("Blockhead", func() { BeforeEach(func() { instanceId = newContainerId() - resp := requestProvision(client, serviceId, planId, instanceId) + resp := requestFabricProvision(client, serviceId, planId, instanceId) Expect(resp.StatusCode).To(Equal(http.StatusCreated)) info, err := cli.ContainerInspect(context.Background(), instanceId) Expect(err).NotTo(HaveOccurred()) - Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("5000/tcp"))) + Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("8545/tcp"))) }) AfterEach(func() { @@ -371,12 +377,12 @@ var _ = Describe("Blockhead", func() { BeforeEach(func() { instanceId = newContainerId() - resp := requestProvision(client, serviceId, planId, instanceId) + resp := requestFabricProvision(client, serviceId, planId, instanceId) Expect(resp.StatusCode).To(Equal(http.StatusCreated)) info, err := cli.ContainerInspect(context.Background(), instanceId) Expect(err).NotTo(HaveOccurred()) - Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("5000/tcp"))) + Expect(info.Config.ExposedPorts).To(HaveKey(nat.Port("8545/tcp"))) }) AfterEach(func() { @@ -397,7 +403,7 @@ var _ = Describe("Blockhead", func() { json.Unmarshal(body, &bindingResults) creds := bindingResults.Credentials.(map[string]interface{}) containerInfo := creds["ContainerInfo"].(map[string]interface{}) - Expect(containerInfo["Bindings"]).To(HaveKey("5000")) + Expect(containerInfo["Bindings"]).To(HaveKey("8545")) nodeInfo := creds["NodeInfo"].(map[string]interface{}) Expect(nodeInfo["Account"]).NotTo(Equal("")) Expect(nodeInfo["ContractAddress"]).NotTo(Equal("")) @@ -451,6 +457,41 @@ func requestProvision(client *http.Client, serviceId, planId, instanceId string) return resp } +func requestFabricProvision(client *http.Client, serviceId, planId, instanceId string) *http.Response { + request := struct { + ServiceId string `json:"service_id"` + PlanId string `json:"plan_id"` + Params interface{} `json:"parameters"` + }{ + ServiceId: serviceId, + PlanId: planId, + Params: struct { + UserID string `json:"user_id"` + Channel string `json:"channel"` + }{ + UserID: "User1", + Channel: "mychannel", + }, + } + + payload := new(bytes.Buffer) + json.NewEncoder(payload).Encode(request) + + provisionURL := fmt.Sprintf("%s/v2/service_instances/%s", serverAddress, instanceId) + req, err := http.NewRequest("PUT", provisionURL, payload) + Expect(err).NotTo(HaveOccurred()) + req.SetBasicAuth("test", "test") + req.Header.Add("X-Broker-API-Version", "2.0") + req.Header.Add("Content-Type", "application/json") + + var resp *http.Response + Eventually(func() error { + resp, err = client.Do(req) + return err + }).Should(Succeed()) + return resp +} + func requestDeprovision(client *http.Client, serviceId, planId, instanceId string) *http.Response { payload := new(bytes.Buffer) json.NewEncoder(payload).Encode("{}") diff --git a/pkg/broker/broker.go b/pkg/broker/broker.go index c8974b68..80f0d786 100644 --- a/pkg/broker/broker.go +++ b/pkg/broker/broker.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "io" "io/ioutil" "net/http" @@ -29,6 +30,11 @@ type BindResponse struct { NodeInfo *deployer.NodeInfo } +type Fab3ProvisionArgs struct { + UserID string `json:"user_id"` + Channel string `json:"channel"` +} + func NewBlockheadBroker(logger lager.Logger, state *config.State, manager containermanager.ContainerManager, deployer deployer.Deployer) BlockheadBroker { return BlockheadBroker{ state: state, @@ -88,10 +94,32 @@ func (b BlockheadBroker) Provision(ctx context.Context, instanceID string, detai return brokerapi.ProvisionedServiceSpec{}, errors.New("plan not found") } + env := []string{} + + if service.Type == config.FABRIC { + logger.Info("Service is of type Fabric") + userInfo := &Fab3ProvisionArgs{} + err := json.Unmarshal(details.RawParameters, userInfo) + if err != nil { + return brokerapi.ProvisionedServiceSpec{}, fmt.Errorf("malformed or missing provision parameters: %s", err.Error()) + } + + if userInfo.UserID == "" { + return brokerapi.ProvisionedServiceSpec{}, errors.New("user id is required at provision") + } + env = append(env, fmt.Sprintf("FABPROXY_USER=%s", userInfo.UserID)) + + if userInfo.Channel == "" { + return brokerapi.ProvisionedServiceSpec{}, errors.New("channel is required at provision") + } + env = append(env, fmt.Sprintf("FABPROXY_CHANNEL=%s", userInfo.Channel)) + } + containerConfig := containermanager.ContainerConfig{ Name: instanceID, Image: plan.Image, ExposedPorts: plan.Ports, + Env: env, } return brokerapi.ProvisionedServiceSpec{}, b.manager.Provision(ctx, containerConfig) diff --git a/pkg/broker/broker_test.go b/pkg/broker/broker_test.go index d53477f0..1d80cc8f 100644 --- a/pkg/broker/broker_test.go +++ b/pkg/broker/broker_test.go @@ -220,6 +220,90 @@ var _ = Describe("Broker", func() { Expect(config.ExposedPorts).To(ConsistOf("1234")) Expect(config.Image).To(Equal("some-image")) }) + + Context("when the service is of type FABRIC", func() { + BeforeEach(func() { + fabricService := config.Service{ + Name: "fabric", + Type: config.FABRIC, + Description: "desc", + DisplayName: "display-name", + Tags: []string{"fab3", "fabric"}, + Plans: make(map[string]*config.Plan), + } + + plan := config.Plan{ + Name: "free", + Image: "some-image", + Ports: []string{"1234"}, + Description: "free-trial", + } + + fabricService.Plans["plan-id"] = &plan + + free = true + expectedService = brokerapi.Service{ + ID: "fabric-service-id", + Name: "fabric", + Description: "desc", + Bindable: true, + Tags: []string{"fab3", "fabric"}, + Metadata: &brokerapi.ServiceMetadata{ + DisplayName: "display-name", + }, + Plans: []brokerapi.ServicePlan{ + brokerapi.ServicePlan{ + ID: "plan-id", + Name: "free", + Description: "free-trial", + Free: &free, + }, + }, + } + + servicesMap[expectedService.ID] = &fabricService + }) + + It("passes along the user id provided at provision time", func() { + provisionDetails := brokerapi.ProvisionDetails{ + ServiceID: "fabric-service-id", + PlanID: "plan-id", + RawParameters: []byte(`{"user_id":"user1", "channel":"mychannel"}`), + } + + expectedEnv := []string{"FABPROXY_USER=user1", "FABPROXY_CHANNEL=mychannel"} + _, err := blockhead.Provision(ctx, "some-instance", provisionDetails, false) + Expect(err).NotTo(HaveOccurred()) + + Expect(fakeManager.ProvisionCallCount()).To(Equal(1)) + + _, config := fakeManager.ProvisionArgsForCall(0) + Expect(config.Name).To(Equal("some-instance")) + Expect(config.ExposedPorts).To(ConsistOf("1234")) + Expect(config.Image).To(Equal("some-image")) + Expect(config.Env).To(Equal(expectedEnv)) + }) + + It("returns an error if parameters or json are missing", func() { + provisionDetails := brokerapi.ProvisionDetails{ + ServiceID: "fabric-service-id", + PlanID: "plan-id", + } + _, err := blockhead.Provision(ctx, "some-instance", provisionDetails, false) + Expect(err).To(HaveOccurred()) + }) + + It("returns an error if user_is is missing", func() { + + provisionDetails := brokerapi.ProvisionDetails{ + ServiceID: "fabric-service-id", + PlanID: "plan-id", + RawParameters: []byte("{}"), + } + _, err := blockhead.Provision(ctx, "some-instance", provisionDetails, false) + Expect(err).To(HaveOccurred()) + }) + }) }) Context("Deprovision", func() { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index de52efdb..9a3f47f7 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -246,13 +246,59 @@ var _ = Describe("Config", func() { }) }) + Context("when there is service with multiple plans", func() { + var anotherExpectedService config.Service + BeforeEach(func() { + serviceFilePath = fmt.Sprintf("%s/service_config_multiple_plans.json", servicePath) + copy("assets/services/service_config_multiple_plans.json", serviceFilePath) + + anotherExpectedService = config.Service{ + Name: "name-2", + Description: "desc", + DisplayName: "display-name", + Tags: []string{"ethereum", "geth"}, + } + + planMap := make(map[string]config.Plan) + expectedPlan1 := config.Plan{ + Name: "plan-name-2", + Image: "image", + Ports: []string{"1234"}, + Description: "plan-desc", + } + planMap["uuid-4"] = expectedPlan1 + + expectedPlan2 := config.Plan{ + Name: "plan-name-3", + Image: "image-2", + Ports: []string{"1234"}, + Description: "plan-desc-2", + } + planMap["uuid-5"] = expectedPlan2 + + anotherExpectedService.Plans = planMap + expectedServices["uuid-3"] = anotherExpectedService + + }) + + It("should have the both plan in the second service", func() { + parsedState, err := config.NewState(configPath, servicePath) + Expect(err).NotTo(HaveOccurred()) + + Expect(parsedState.Services).To(ConsistOf( + utils.EquivalentService(&expectedService), + utils.EquivalentService(&anotherExpectedService), + )) + }) + }) + Context("when there is a service file with wrong type", func() { BeforeEach(func() { serviceFilePath = fmt.Sprintf("%s/bad_service_config.json", servicePath) copy("assets/bad_services/bad_service_config.json", serviceFilePath) }) - It("should have one service", func() { + It("should return an error", func() { _, err := config.NewState(configPath, servicePath) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Service type not found")) diff --git a/pkg/containermanager/container_manager.go b/pkg/containermanager/container_manager.go index 237e634b..1bd453d3 100644 --- a/pkg/containermanager/container_manager.go +++ b/pkg/containermanager/container_manager.go @@ -8,6 +8,7 @@ type ContainerConfig struct { Name string Image string ExposedPorts []string + Env []string } type BindConfig struct { diff --git a/pkg/containermanager/docker/docker_manager.go b/pkg/containermanager/docker/docker_manager.go index 5f6b7e50..68851d7e 100644 --- a/pkg/containermanager/docker/docker_manager.go +++ b/pkg/containermanager/docker/docker_manager.go @@ -142,6 +142,7 @@ func createContainerConfig(containerConfig containermanager.ContainerConfig) (*c Image: containerConfig.Image, Volumes: map[string]struct{}{}, NetworkDisabled: false, + Env: containerConfig.Env, } return config, nil diff --git a/pkg/containermanager/docker/docker_manager_test.go b/pkg/containermanager/docker/docker_manager_test.go index 3a951530..261e9705 100644 --- a/pkg/containermanager/docker/docker_manager_test.go +++ b/pkg/containermanager/docker/docker_manager_test.go @@ -31,6 +31,7 @@ var _ = Describe("DockerManager", func() { Name: "some-name", Image: "some-image", ExposedPorts: []string{"1234", "2345/udp"}, + Env: []string{"SOME_ENV_VAR=ENV_VAR"}, } }) @@ -58,6 +59,7 @@ var _ = Describe("DockerManager", func() { Expect(config).NotTo(BeNil()) Expect(config.ExposedPorts).To(HaveKey(nat.Port("1234/tcp"))) Expect(config.ExposedPorts).To(HaveKey(nat.Port("2345/udp"))) + Expect(config.Env).To(ConsistOf("SOME_ENV_VAR=ENV_VAR")) Expect(hostConfig).NotTo(BeNil()) Expect(hostConfig.PublishAllPorts).To(BeTrue()) Expect(hostConfig.Privileged).To(BeFalse()) diff --git a/services/fabric.json b/services/fabric.json index 463d24ee..067b9ac6 100644 --- a/services/fabric.json +++ b/services/fabric.json @@ -9,11 +9,18 @@ "display_name": "Fabric Proxy 0.1", "plans": [ { - "name": "free", + "name": "fab3-org1", "free": true, - "image": "nimak/evm-proxy:latest", - "description": "Free Trial", - "ports": ["5000"] + "image": "swayr/fab3-org1", + "description": "Fab3 org 1", + "ports": ["8545"] + }, + { + "name": "fab3-org2", + "free": true, + "image": "swayr/fab3-org2", + "description": "Fab3 org 2", + "ports": ["8545"] } ] }