Skip to content

Commit

Permalink
Merge pull request #105 from JameelB/update-cli-latest-apb
Browse files Browse the repository at this point in the history
[AEROGEAR-2598] Update CLI to work with the latest APB changes
  • Loading branch information
maleck13 committed Apr 30, 2018
2 parents 7eb3d46 + 41f068f commit 087c8d4
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 106 deletions.
50 changes: 40 additions & 10 deletions pkg/cmd/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package cmd

import (
"encoding/json"

"fmt"

"io"
Expand Down Expand Up @@ -45,8 +44,10 @@ func NewIntegrationCmd(scClient sc.Interface, k8Client kubernetes.Interface, out
return &IntegrationCmd{scClient: scClient, k8Client: k8Client, BaseCmd: &BaseCmd{Out: output.NewRenderer(out)}}
}

func createBindingObject(consumer, provider, bindingName, instance string, params map[string]interface{}, secretName string) (*v1beta1.ServiceBinding, error) {
pdata, err := json.Marshal(params)
func createBindingObject(consumer, provider, bindingName, instance string, bindParams *ServiceParams, secretName string) (*v1beta1.ServiceBinding, error) {
parsedBindParams := buildBindParams(bindParams)
pdata, err := json.Marshal(parsedBindParams)

if err != nil {
return nil, err
}
Expand Down Expand Up @@ -164,10 +165,36 @@ If both the --no-wait and --auto-redeploy flags are set to true, --auto-redeploy
if err != nil {
return errors.WithStack(err)
}
//TODO review how we build the params
bindParams := buildBindParams(providerServiceName, consumerServiceName)
// Get available bind parameters from the provider cluster service plan
clusterServiceClass, err := findServiceClassByName(bc.scClient, providerServiceName)
if err != nil {
return errors.WithStack(err)
}
clusterServicePlan, err := findServicePlanByNameAndClass(bc.scClient, "default", clusterServiceClass.Name)
if err != nil {
return errors.WithStack(err)
}

bindParams := &ServiceParams{}
if err := json.Unmarshal(clusterServicePlan.Spec.ServiceBindingCreateParameterSchema.Raw, bindParams); err != nil {
return errors.WithStack(err)
}

flagParams, err := cmd.Flags().GetStringArray("params")
if err != nil {
return errors.WithStack(err)
}

// Get bind parameters value from user input
bindParams, err = GetParams(flagParams, bindParams)
if err != nil {
return errors.WithStack(err)
}

objectName := objectName(consumerSvcInstName, providerSvcInstName)
preset := podPreset(objectName, objectName, providerServiceName, consumerServiceName)

// Create Pod Preset for service
if _, err := bc.k8Client.SettingsV1alpha1().PodPresets(namespace).Create(preset); err != nil {
return errors.Wrap(err, "failed to create pod preset for service ")
}
Expand Down Expand Up @@ -242,6 +269,8 @@ If both the --no-wait and --auto-redeploy flags are set to true, --auto-redeploy
}
cmd.PersistentFlags().Bool("no-wait", false, "--no-wait will cause the command to exit immediately after a successful response instead of waiting until the binding is complete")
cmd.PersistentFlags().Bool("auto-redeploy", false, "--auto-redeploy=true will cause a backing deployment to be rolled out")
cmd.PersistentFlags().StringArrayP("params", "p", []string{}, "set the parameters needed to set up the integration programatically rather than being prompted for them: -p PARAM1=val -p PARAM2=val2")

return cmd
}

Expand Down Expand Up @@ -410,11 +439,12 @@ func (bc *IntegrationCmd) ListIntegrationsCmd() *cobra.Command {
}

// TODO review how we build params. This is still POC
func buildBindParams(from string, to string) map[string]interface{} {
var p = map[string]interface{}{}
func buildBindParams(bindParams *ServiceParams) map[string]interface{} {
params := map[string]interface{}{}

if from == ServiceNameKeycloak {
p["service"] = to
for k, v := range bindParams.Properties {
params[k] = v["value"]
}
return p

return params
}
90 changes: 87 additions & 3 deletions pkg/cmd/integration_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package cmd_test

import (
"bytes"
"encoding/json"
"fmt"
"testing"

"bytes"

"github.com/aerogear/mobile-cli/pkg/apis/servicecatalog/v1beta1"
"github.com/aerogear/mobile-cli/pkg/client/servicecatalog/clientset/versioned"
scFake "github.com/aerogear/mobile-cli/pkg/client/servicecatalog/clientset/versioned/fake"
Expand Down Expand Up @@ -117,6 +117,34 @@ func TestIntegrationCmd_CreateIntegrationCmd(t *testing.T) {
fake.AddReactor("get", "clusterserviceclasses", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &v1beta1.ClusterServiceClass{Spec: v1beta1.ClusterServiceClassSpec{ExternalMetadata: &runtime.RawExtension{Raw: []byte(`{"serviceName":"test"}`)}}}, nil
})
fake.AddReactor("list", "clusterserviceclasses", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
externalData := cmd.ExternalServiceMetaData{
ServiceName: "test",
}
data, _ := json.Marshal(externalData)
return true, &v1beta1.ClusterServiceClassList{Items: []v1beta1.ClusterServiceClass{
{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
Spec: v1beta1.ClusterServiceClassSpec{
ExternalMetadata: &runtime.RawExtension{Raw: data},
},
},
}}, nil
})
fake.AddReactor("list", "clusterserviceplans", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
params := &cmd.ServiceParams{Required: []string{"CLIENT_NAME"}, Properties: map[string]map[string]interface{}{"CLIENT_NAME": {"value": "", "default": "test-client-name"}}}
b, _ := json.Marshal(params)
return true, &v1beta1.ClusterServicePlanList{Items: []v1beta1.ClusterServicePlan{
{Spec: v1beta1.ClusterServicePlanSpec{
ServiceBindingCreateParameterSchema: &runtime.RawExtension{Raw: b},
ClusterServiceClassRef: v1beta1.ClusterObjectReference{Name: "test"},
ExternalName: "default"},
},
},
}, nil
})
return fake, fakeWatch, []runtime.Object{
defaultServiceBinding,
}
Expand Down Expand Up @@ -163,6 +191,34 @@ func TestIntegrationCmd_CreateIntegrationCmd(t *testing.T) {
fake.AddReactor("get", "clusterserviceclasses", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &v1beta1.ClusterServiceClass{Spec: v1beta1.ClusterServiceClassSpec{ExternalMetadata: &runtime.RawExtension{Raw: []byte(`{"serviceName":"test"}`)}}}, nil
})
fake.AddReactor("list", "clusterserviceclasses", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
externalData := cmd.ExternalServiceMetaData{
ServiceName: "test",
}
data, _ := json.Marshal(externalData)
return true, &v1beta1.ClusterServiceClassList{Items: []v1beta1.ClusterServiceClass{
{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
Spec: v1beta1.ClusterServiceClassSpec{
ExternalMetadata: &runtime.RawExtension{Raw: data},
},
},
}}, nil
})
fake.AddReactor("list", "clusterserviceplans", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
params := &cmd.ServiceParams{Required: []string{"CLIENT_NAME"}, Properties: map[string]map[string]interface{}{"CLIENT_NAME": {"value": "", "default": "test-client-name"}}}
b, _ := json.Marshal(params)
return true, &v1beta1.ClusterServicePlanList{Items: []v1beta1.ClusterServicePlan{{
Spec: v1beta1.ClusterServicePlanSpec{
ServiceBindingCreateParameterSchema: &runtime.RawExtension{Raw: b},
ClusterServiceClassRef: v1beta1.ClusterObjectReference{Name: "test"},
ExternalName: "default"},
},
},
}, nil
})
return fake, fakeWatch, []runtime.Object{
defaultServiceBinding,
}
Expand Down Expand Up @@ -234,6 +290,34 @@ func TestIntegrationCmd_CreateIntegrationCmd(t *testing.T) {
fake.AddReactor("get", "clusterserviceclasses", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &v1beta1.ClusterServiceClass{Spec: v1beta1.ClusterServiceClassSpec{ExternalMetadata: &runtime.RawExtension{Raw: []byte(`{"serviceName":"test"}`)}}}, nil
})
fake.AddReactor("list", "clusterserviceclasses", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
externalData := cmd.ExternalServiceMetaData{
ServiceName: "test",
}
data, _ := json.Marshal(externalData)
return true, &v1beta1.ClusterServiceClassList{Items: []v1beta1.ClusterServiceClass{
{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
Spec: v1beta1.ClusterServiceClassSpec{
ExternalMetadata: &runtime.RawExtension{Raw: data},
},
},
}}, nil
})
fake.AddReactor("list", "clusterserviceplans", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
params := &cmd.ServiceParams{Required: []string{"CLIENT_NAME"}, Properties: map[string]map[string]interface{}{"CLIENT_NAME": {"value": "", "default": "test-client-name"}}}
b, _ := json.Marshal(params)
return true, &v1beta1.ClusterServicePlanList{Items: []v1beta1.ClusterServicePlan{{
Spec: v1beta1.ClusterServicePlanSpec{
ServiceBindingCreateParameterSchema: &runtime.RawExtension{Raw: b},
ClusterServiceClassRef: v1beta1.ClusterObjectReference{Name: "test"},
ExternalName: "default"},
},
},
}, nil
})
return fake, fakeWatch, []runtime.Object{
defaultServiceBinding,
}
Expand All @@ -254,7 +338,7 @@ func TestIntegrationCmd_CreateIntegrationCmd(t *testing.T) {
return fake
},
Args: []string{"keycloak", "fh-sync-server"},
Flags: []string{"--namespace=test", "--auto-redeploy=true", "--no-wait=true"},
Flags: []string{"--namespace=test", "-pCLIENT_NAME=test", "--auto-redeploy=true", "--no-wait=true"},
},
}

Expand Down
99 changes: 99 additions & 0 deletions pkg/cmd/params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package cmd

import (
"bufio"
"fmt"
"os"
"strings"

"github.com/pkg/errors"
)

// ServiceParams for creating integration and binding
type ServiceParams struct {
AdditionalProperties bool `json:"additionalProperties"`
Properties map[string]map[string]interface{} `json:"properties"`
Required []string `json:"required"`
Type string `json:"type"`
}

func parseParams(keyVals []string) (map[string]string, error) {
params := map[string]string{}
for _, p := range keyVals {
kv := strings.Split(p, "=")
if len(kv) != 2 {
return nil, NewIncorrectParameterFormat("key value pairs are needed failed to find one: " + p)
}
params[strings.TrimSpace(kv[0])] = kv[1]
}
return params, nil
}

func isRequired(params ServiceParams, key string) bool {
for _, r := range params.Required {
if r == key {
return true
}
}
return false
}

// GetParams - Gets the service parameters (i.e. for provision/bind service) from the params
// flag or as a user input
func GetParams(flagParams []string, params *ServiceParams) (*ServiceParams, error) {
parsedParams, err := parseParams(flagParams)
if err != nil {
return params, errors.WithStack(err)
}

if len(parsedParams) > 0 {
for k, v := range params.Properties {
defaultVal := v["default"]
if pVal, ok := parsedParams[k]; !ok && isRequired(*params, k) || isRequired(*params, k) && pVal == "" {
if defaultVal != nil {
//use default
v["value"] = defaultVal
continue
}
return params, errors.New(fmt.Sprintf("missing required parameter %s", k))
}
v["value"] = parsedParams[k]
params.Properties[k] = v
}
} else {
scanner := bufio.NewScanner(os.Stdin)
for k, v := range params.Properties {
validInput := false
val := ""
for validInput == false {
questionFormat := "Set value for %s [default value: %s, required: %v]"
if v["default"] != nil {
fmt.Println(fmt.Sprintf(questionFormat, k, v["default"], isRequired(*params, k)))
} else {
fmt.Println(fmt.Sprintf(questionFormat, k, "<no default value>", isRequired(*params, k)))
}
scanner.Scan()

val = strings.TrimSpace(scanner.Text())

if len(val) > 0 {
validInput = true
}
if validInput == false && val == "" && v["default"] != nil {
val = v["default"].(string)
validInput = true
}
if validInput == false && val == "" && !isRequired(*params, k) {
validInput = true
}
if validInput == false {
fmt.Println("Invalid option for required field.")
}
}
v["value"] = val
params.Properties[k] = v
fmt.Println(fmt.Sprintf("Value for %s set to: %s", k, val))
}
}
return params, nil
}
Loading

0 comments on commit 087c8d4

Please sign in to comment.