Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for creating component from service catalog #413

Merged
merged 4 commits into from
Aug 29, 2018

Conversation

surajnarwade
Copy link
Contributor

@surajnarwade surajnarwade commented Apr 24, 2018

Fixes #266

Signed-off-by: Suraj Narwade surajnarwade353@gmail.com

This PR adds the support for creating services from service catalog. for this support, we have used
client from kubernetes-incubator/service-catalog.
Currently, we are supporting template service broker only. In future, we will add support for
other brokers as well.

We have added entire new command set for this purpose.

  • to check available services in service catalog
$ odo service catalog
  • to create service,
$ odo service create mysql-persistent
  • to delete service,
$ odo service delete mysql-persistent
  • to list the current provisioned services with their status
$ odo service list

@surajnarwade surajnarwade changed the title Add support for creating component from service catalog [WIP]Add support for creating component from service catalog Apr 24, 2018
@surajnarwade surajnarwade added do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. Required by Prow. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. Required by Prow. labels Apr 24, 2018
@kadel
Copy link
Member

kadel commented Apr 24, 2018

please add link to the issue

@surajnarwade
Copy link
Contributor Author

Fixes #266

@surajnarwade
Copy link
Contributor Author

Prerequisite to run/test this PR:

  • Run following commands,
minishift config set openshift-version v3.7.1

export MINISHIFT_ENABLE_EXPERIMENTAL=y

minishift config set iso-url centos

minishift start --service-catalog

@surajnarwade surajnarwade removed do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. Required by Prow. do not review do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. Required by Prow. labels Apr 30, 2018
@surajnarwade surajnarwade changed the title [WIP]Add support for creating component from service catalog Add support for creating component from service catalog Apr 30, 2018
@@ -9,55 +9,68 @@ import (
)

// List lists all the available component types
func List(client *occlient.Client) ([]string, error) {
func List(client *occlient.Client) (finalList map[string][]string, err error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please don't use named return values. It makes it hard to read :-(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure

}

return result, nil
}

// Exists returns true if the given component type is valid, false if not
func Exists(client *occlient.Client, componentType string) (bool, error) {
func Exists(client *occlient.Client, componentType string) (bool, error, string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment explaining what return values are. Especially new returned string

func (c *Client) CreateServiceInstance(componentName string, componentType string, labels map[string]string, applicationName string, projectName string) error {
// Patching Template with ODO specific labels
labelPatch := fmt.Sprintf(`{"labels": {"app.kubernetes.io/component-name": "%s", "app.kubernetes.io/component-type": "%s", "app.kubernetes.io/name": "%s"}}`, componentName, componentType, applicationName)
_, err := c.templateClient.Templates("openshift").Patch(componentType, types.StrategicMergePatchType, []byte(labelPatch))
Copy link
Member

@kadel kadel Apr 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

won't this permanently modify templates saved in openshift namespace?

Copy link
Member

@kadel kadel Apr 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if service is from some other broker than openshift template broker?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't get second question

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function is called CreateServiceInstance, but is using OpenShift templates.
It is really confusing :-(
There can be multiple different Service brokers in Service Catalog.

@@ -892,6 +899,21 @@ func (c *Client) GetClusterServiceClasses() ([]scv1beta1.ClusterServiceClass, er
return classList.Items, nil
}

func (c *Client) CreateServiceInstance(componentName string, componentType string, labels map[string]string, applicationName string, projectName string) error {
Copy link
Member

@kadel kadel Apr 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are in occlient package it should have componentName or componentType variables


"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
types "k8s.io/apimachinery/pkg/types"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imports should be logically grouped together

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that was auto goimports in goland :(

cmd/create.go Outdated
@@ -127,6 +127,10 @@ A full list of component types that can be deployed is available using: 'odo com
checkError(err, fmt.Sprintf("failed to push component: %v", componentName))

fmt.Printf("Component '%s' was created.\n", componentName)
} else if len(catalogType) != 0 && catalogType == "template" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if len(catalogType) != 0 and catalogType is different from "template"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

other catalog type is imagestream so other conditions will handle that

@kadel
Copy link
Member

kadel commented Apr 30, 2018

It breaks for everything if cluster doesn't have service catalog.
It this case regular s2i should work.

▶ odo create wildfly
User "developer" cannot list clusterserviceclasses.servicecatalog.k8s.io at the cluster scope: User "developer" cannot list all clusterserviceclasses.servicecatalog.k8s.io in the cluster (get clusterserviceclasses.servicecatalog.k8s.io)

@kadel
Copy link
Member

kadel commented Apr 30, 2018

minishift config set openshift-version v3.7.1

why is 3.7.1 required?

minishift config set iso-url centos

why is centos required?

@surajnarwade
Copy link
Contributor Author

@kadel , service catalog & ansible service broker addons works well with this version

return errors.Wrap(err, "unable to patch template")
}
// Creating Service Instance
_, err = c.serviceCatalogClient.ServiceInstances(projectName).Create(&scv1beta1.ServiceInstance{metav1.TypeMeta{Kind: "ServiceInstance", APIVersion: "servicecatalog.k8s.io/v1beta1"}, metav1.ObjectMeta{Finalizers: []string{"kubernetes-incubator/service-catalog"}, Name: componentName, Namespace: "myproject", Labels: labels}, scv1beta1.ServiceInstanceSpec{UserInfo: &scv1beta1.UserInfo{Username: "developer"}, PlanReference: scv1beta1.PlanReference{ClusterServiceClassExternalName: componentType}}, scv1beta1.ServiceInstanceStatus{}})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add break ServiceInstance struct to multiple lines,it is hard to see what is going on there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are passing labels there. If you can pass labels, why is the patching needed in the previous step?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I am gonna remove this label, I was just playing around it. this label will only apply to serviceinstance. But we want label on every resource that will be created for example, deploymentconfig, services, routes

@@ -892,6 +899,21 @@ func (c *Client) GetClusterServiceClasses() ([]scv1beta1.ClusterServiceClass, er
return classList.Items, nil
}

func (c *Client) CreateServiceInstance(componentName string, componentType string, labels map[string]string, applicationName string, projectName string) error {
// Patching Template with ODO specific labels
labelPatch := fmt.Sprintf(`{"labels": {"app.kubernetes.io/component-name": "%s", "app.kubernetes.io/component-type": "%s", "app.kubernetes.io/name": "%s"}}`, componentName, componentType, applicationName)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please break this to multiple lines

Copy link
Member

@kadel kadel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not able to create from catalog

▶ odo create jenkins-persistent
User "developer" cannot patch templates.template.openshift.io in the namespace "openshift": User "developer" cannot "patch" "templates.template.openshift.io" with name "jenkins-persistent" in project "openshift" (patch templates.template.openshift.io jenkins-persistent)

@surajnarwade surajnarwade force-pushed the catalog branch 2 times, most recently from ab1de12 to 1153b25 Compare May 2, 2018 08:46
cmd/service.go Outdated
},
}

//var serviceCatalogCmd = &cobra.Command{
Copy link
Contributor

@anmolbabu anmolbabu Aug 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Commented out?
I tried this based on #413 (comment) but doesn't seem to be working

@surajnarwade
Copy link
Contributor Author

Commands to test:

* odo catalog list components

* odo catalog list services

* odo catalog search components

* odo catalog search services

* odo service create <service_name>

* odo service delete <service_name>

* odo service list

cc @anmolbabu @cdrage @mik-dass

@jorgemoralespou
Copy link
Contributor

I would add to the list of tests the ones for a component, as the possible components that can be used are from the catalog.

* odo create <component_type> <component_name>

* odo delete <component_name>

* odo list

Copy link
Contributor

@anmolbabu anmolbabu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pleas also add e2e tests

I tried to setup minishift with service catalog enabled but I see consistent failures with it :

  1. sometimes at the time of pulling minishift-centos iso
  2. sometimes at the time of pulling origin container image
  3. sometimes at time of starting openshift cluster

So, couldn't verify the PR

Run: func(cmd *cobra.Command, args []string) {
client := getOcClient()
catalogList, err := svc.ListCatalog(client)
checkError(err, "unable to list services because Service Catalog is not enabled in your cluster")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it better to only say "unable to list services" and leave the other part of the error to come from openshift?
If there's a network error for example, I feel it might be misleading to suggest "Service Catalog is not enabled in your cluster"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

our user will not understand openshift error (so we have to simplify it)
if there is network error, process will not reach till this stage :P

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm so the occlient creation would fail is what you are saying right? So can there be any other error like permissions/authorisation for using service catalog issue ?

If not please ignore this comment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this error message is okay for this situation

// Service Instance
glog.V(4).Infof("Deleting Service Instance")
// Listing out serviceInstance because `DeleteCollection` method don't work on serviceInstance
svcCatList, err := c.serviceCatalogClient.ServiceInstances(c.namespace).List(metav1.ListOptions{LabelSelector: selector})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done 👍

return values, nil
}

// GetServiceInstanceList returns list service instances
func (c *Client) GetServiceInstanceList(project string, selector string) ([]scv1beta1.ServiceInstance, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we call project as namespace ?
Just trying to avoid multiple terminologies for same thing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is fine, I guess

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

if !reflect.DeepEqual(createdServiceInstance.Labels, tt.args.labels) {
t.Errorf("labels in created serviceInstance is not matching expected labels, expected: %v, got: %v", tt.args.labels, createdServiceInstance.Labels)
}
if !reflect.DeepEqual(createdServiceInstance.Name, tt.args.componentName) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have just == comparisons for basic types ?

@@ -0,0 +1,133 @@
package service
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1
Doesn't look like a lot of validations in this file
But, may be for code-coverage :)

@surajnarwade surajnarwade changed the title Add support for creating component from service catalog [WIP]Add support for creating component from service catalog Aug 28, 2018
@surajnarwade surajnarwade added do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. Required by Prow. and removed do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. Required by Prow. labels Aug 28, 2018
@surajnarwade surajnarwade changed the title [WIP]Add support for creating component from service catalog Add support for creating component from service catalog Aug 28, 2018
Copy link
Contributor

@anmolbabu anmolbabu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

But may be you need to add unit test for pkg/service/service.go + also e2e tests

Copy link
Contributor

@cmoulliard cmoulliard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

odo service create mysql-persistent

I suppose that you assume here that mysql-persistent corresponds to the class of the service to be used to create a ServiceInstance ? How will the user be able to define the serviceInstance name to be used ? How will it be possible to define the parameters of the service (e.g. DB_*** parameters) ? @surajnarwade

func (c *Client) GetClusterServiceClasses() ([]scv1beta1.ClusterServiceClass, error) {
classList, err := c.serviceCatalogClient.ClusterServiceClasses().List(metav1.ListOptions{})
// TODO: Remove `FieldSelector` from ListOptions when we are confident with Ansible Service Broker
classList, err := c.serviceCatalogClient.ClusterServiceClasses().List(metav1.ListOptions{FieldSelector: "spec.clusterServiceBrokerName=template-service-broker"})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OSB is working pretty well for me

minishift v1.23.0+91235ee

minishift config set memory 6G
minishift config set openshift-version v3.10.0
minsishift addons enable admin-user

MINISHIFT_ENABLE_EXPERIMENTAL=y minishift start --extra-clusterup-flags="--enable=*,service-catalog,automation-service-broker"

if err != nil {
return nil, errors.Wrap(err, "unable to list cluster service classes")
}
return classList.Items, nil
}

// CreateServiceInstance creates service instance from service catalog
func (c *Client) CreateServiceInstance(componentName string, componentType string, labels map[string]string) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that your createServiceInstance function doesn't include the spec.parameters

Example : https://github.com/snowdrop/cloud-native-backend/blob/master/openshift/mysql_serviceinstance.yml#L9-L13

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jorgemoralespou
Copy link
Contributor

odo service create mysql-persistent without the required parameters should yield an error, and user would need to provide them.

Typically every service will require some configuration, so user will need to odo service describe <service_name> to get the parameter configuration so that the user can create the service.

In reality this will get horribly complex because:

  • When creating a service, one might need to select a plan
  • When creating a service, externalName is not what defines the service id to use, but it's just a text that can be repeated multiple times.

This task it's turning more complex than initially envisioned for the way how servicecatalog API works. I would defer the work or think that we should revisit this in the near future with many changes.
cc/ @kadel

@surajnarwade
Copy link
Contributor Author

surajnarwade commented Aug 29, 2018

@jorgemoralespou , @cmoulliard :

This PR just demonstrate that we can create the basic service from service catalog. Also, this PR does not conflict with odo's core functionality. Upon merging this PR:

Next possible steps are:

  • Add respective plans and parameters to odo catalog list services

  • Modify odo service create service_name to accept plans and parameters, either by arguments or interactive mode

  • Upon running odo link <target_component> --service <service_name> will inject secret into env of deploymentconfig and restart it

WDYT ?

@cmoulliard
Copy link
Contributor

will get horribly complex because

I agree with you. The flow requires that we do :

  • Search about a service and plan
  • Extract from the plan the parameters and customize them (-> under MANIFEST or application.yml file maybe)
  • (out of scope of odo) Select the correct maven dependency to add. For spring Boot -> starter, cloud connector able to process data of the service
  • Create a service instance using parameters customized
  • Generate a Secret with the parameters (= binding)
  • Update the DeploymentConfig to add the secret as EnvFrom
  • Rollout the DeploymentConfig

@jorgemoralespou
Copy link
Contributor

@surajnarwade sounds good to me.

@cmoulliard

Search about a service and plan

odo catalog list service
odo catalog search service

Extract from the plan the parameters and customize them (-> under MANIFEST or application.yml file maybe)

odo catalog describe service

Create a service instance using parameters customized

odo service create -p param1=value1 -p param2=value2

Generate a Secret with the parameters (= binding)
Update the DeploymentConfig to add the secret as EnvFrom
Rollout the DeploymentConfig

odo link

Copy link
Contributor

@anmolbabu anmolbabu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please create a bug to track e2e test and docs only so that we don't lose track of it and link back here

@cmoulliard
Copy link
Contributor

odo service create -p param1=value1 -p param2=value2

this command will work for service containing string's fields but when json or yaml content is required (as this is the case for the keycloak's service), then such info should be defined within a config's application file (MANIFEST, odo-application.yaml)

Copy link
Contributor

@mik-dass mik-dass left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@surajnarwade
Copy link
Contributor Author

@jorgemoralespou @cmoulliard @cdrage , if you all agree, then @anmolbabu can merge this PR
(since we got 3 LGTMs :D )

@jorgemoralespou
Copy link
Contributor

@surajnarwade I can't vote as I can't read the implementation. To me looks good and as part of the discussion we can create follow up issues.

@anmolbabu
Copy link
Contributor

Merging based on previous acks but @surajnarwade please log issue to add documentation and e2e tests

@anmolbabu anmolbabu merged commit bbcf73f into redhat-developer:master Aug 29, 2018
@anmolbabu
Copy link
Contributor

As per our discussion, I have also raised #678 please take it up on priority @surajnarwade

cdrage added a commit that referenced this pull request Aug 29, 2018
@@ -21,15 +22,26 @@ var catalogCmd = &cobra.Command{

var catalogListCmd = &cobra.Command{
Use: "list",
Short: "List all available component & service types.",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should change the sentence so both commands are similar:

Currently it is:

Available Commands:
  components  List all available component types.
  services    Lists all the services from service catalog

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe: Lists all components available
and Lists all services available ?

Example: ` # List all services
odo catalog list services

# Search for a supported services
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Search for a supported service


If service name is not provided, service type value will be used.

A full list of service types that can be deployed is available using: 'odo service catalog'`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are available

exists, err = svc.SvcExists(client, serviceName, applicationName, projectName)
checkError(err, "")
if exists {
fmt.Printf("Service with the name %s already exists in the current application.\n", serviceName)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove with

Use: "delete <service_name>",
Short: "Delete an existing service",
Long: "Delete an existing service",
Example: ` # Delete service named 'mysql-persistent'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete the service

serviceCmd.SetUsageTemplate(cmdUsageTemplate)
serviceCmd.AddCommand(serviceCreateCmd)
serviceCmd.AddCommand(serviceDeleteCmd)
//serviceCmd.AddCommand(serviceCatalogCmd)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove commented out code

glog.V(4).Infof("Selectors used for deletion: %s", selector)
// Service Instance
glog.V(4).Infof("Deleting Service Instance")
// Listing out serviceInstance because `DeleteCollection` method don't work on serviceInstance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add some newline / spacing :( makes everything a bit cluttered up here.

// convert labels to selector
selector := util.ConvertLabelsToSelector(labels)
glog.V(4).Infof("Selectors used for deletion: %s", selector)
// Service Instance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we really need this comment

if err != nil {
return nil, errors.Wrap(err, "unable to list cluster service classes")
}
return classList.Items, nil
}

// CreateServiceInstance creates service instance from service catalog
func (c *Client) CreateServiceInstance(componentName string, componentType string, labels map[string]string) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

//since, service is associated with application, it consist of application label as well
// which we can give as a selector
applicationSelector := util.ConvertLabelsToSelector(labels)
// get service instance list based on given selector
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add newline / spacing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants