From 662ea8306fc34d869494754e0a5f9e961b571657 Mon Sep 17 00:00:00 2001 From: Shubham Minglani Date: Mon, 13 Nov 2017 20:11:42 +0530 Subject: [PATCH] add BuildConfig support This commit adds support for BuildConfigs to the Kedge spec. Now, at the root level, BuildConfigs can be specified with the "buildConfigs:" key, which takes in an array of BuildConfigSpec + ObjectMeta definitions. This commit also adds unit tests for this new behavior. --- pkg/cmd/commands.go | 2 +- pkg/spec/resources.go | 48 +++++++++++ pkg/spec/resources_test.go | 172 +++++++++++++++++++++++++++++++++++++ pkg/spec/types.go | 13 +++ pkg/spec/util.go | 6 ++ 5 files changed, 240 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/commands.go b/pkg/cmd/commands.go index c777e1a17..618a268b0 100644 --- a/pkg/cmd/commands.go +++ b/pkg/cmd/commands.go @@ -64,7 +64,7 @@ func CreateArtifacts(paths []string, generate bool, args ...string) error { for _, runtimeObject := range ros { switch runtimeObject.GetObjectKind().GroupVersionKind().Kind { // If there is at least one OpenShift resource use oc - case "DeploymentConfig", "Route", "ImageStream": + case "DeploymentConfig", "Route", "ImageStream", "BuildConfig": useOC = true break } diff --git a/pkg/spec/resources.go b/pkg/spec/resources.go index ee09b22ea..720b44214 100644 --- a/pkg/spec/resources.go +++ b/pkg/spec/resources.go @@ -22,6 +22,7 @@ import ( "strings" log "github.com/Sirupsen/logrus" + build_v1 "github.com/openshift/origin/pkg/build/apis/build/v1" image_v1 "github.com/openshift/origin/pkg/image/apis/image/v1" os_route_v1 "github.com/openshift/origin/pkg/route/apis/route/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -146,6 +147,26 @@ func fixImageStreams(imageStreams []ImageStreamSpecMod, appName string) ([]Image return imageStreams, nil } +func fixBuildConfigs(buildConfigs []BuildConfigSpecMod, appName string) ([]BuildConfigSpecMod, error) { + + // auto populate name only if one buildConfig is specified without any name + if len(buildConfigs) == 1 && buildConfigs[0].Name == "" { + buildConfigs[0].ObjectMeta.Name = appName + } + + for i, bc := range buildConfigs { + if bc.Name == "" { + return nil, fmt.Errorf("please specify name for app.buildConfigs[%d]", i) + } + + bc.ObjectMeta.Labels = addKeyValueToMap(appLabelKey, appName, bc.ObjectMeta.Labels) + + // this should be the last statement in this for loop + buildConfigs[i] = bc + } + return buildConfigs, nil +} + func fixIngresses(ingresses []IngressSpecMod, appName string) ([]IngressSpecMod, error) { // auto populate name only if one ingress is specified without any name @@ -244,6 +265,12 @@ func (cf *ControllerFields) fixControllerFields() error { return errors.Wrap(err, "unable to fix imageStreams") } + // fix buildConfigs + cf.BuildConfigs, err = fixBuildConfigs(cf.BuildConfigs, cf.Name) + if err != nil { + return errors.Wrap(err, "unable to fix buildConfigs") + } + // fix ingresses cf.Ingresses, err = fixIngresses(cf.Ingresses, cf.Name) if err != nil { @@ -440,6 +467,19 @@ func (app *ControllerFields) createImageStreams() ([]runtime.Object, error) { return imageStreams, nil } +func (app *ControllerFields) createBuildConfigs() ([]runtime.Object, error) { + var buildConfigs []runtime.Object + + for _, b := range app.BuildConfigs { + buildConfig := &build_v1.BuildConfig{ + ObjectMeta: b.ObjectMeta, + Spec: b.BuildConfigSpec, + } + buildConfigs = append(buildConfigs, buildConfig) + } + return buildConfigs, nil +} + // CreateK8sObjects, if given object DeploymentSpecMod, this function reads // them and returns kubernetes objects as list of runtime.Object // If the deployment is using field 'includeResources' then it will @@ -476,6 +516,11 @@ func (app *ControllerFields) CreateK8sObjects() ([]runtime.Object, []string, err return nil, nil, errors.Wrap(err, "unable to create OpenShift ImageStreams") } + bcs, err := app.createBuildConfigs() + if err != nil { + return nil, nil, errors.Wrap(err, "Unable to create OpenShift BuildConfigs") + } + app.PodSpec.Containers, err = populateContainers(app.Containers, app.ConfigMaps, app.Secrets) if err != nil { return nil, nil, errors.Wrapf(err, "deployment %q", app.Name) @@ -534,6 +579,9 @@ func (app *ControllerFields) CreateK8sObjects() ([]runtime.Object, []string, err objects = append(objects, iss...) log.Debugf("app: %s, imageStreams: %s\n", app.Name, spew.Sprint(iss)) + objects = append(objects, bcs...) + log.Debugf("app: %s, buildConfig: %s\n", app.Name, spew.Sprint(bcs)) + objects = append(objects, configMap...) log.Debugf("app: %s, configMap: %s\n", app.Name, spew.Sprint(configMap)) diff --git a/pkg/spec/resources_test.go b/pkg/spec/resources_test.go index 871434a3b..88bff999e 100644 --- a/pkg/spec/resources_test.go +++ b/pkg/spec/resources_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/davecgh/go-spew/spew" + build_v1 "github.com/openshift/origin/pkg/build/apis/build/v1" image_v1 "github.com/openshift/origin/pkg/image/apis/image/v1" os_route_v1 "github.com/openshift/origin/pkg/route/apis/route/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -140,6 +141,85 @@ func TestFixConfigMaps(t *testing.T) { } } +func TestFixBuildConfigs(t *testing.T) { + appName := "testAppName" + tests := []struct { + name string + input []BuildConfigSpecMod + output []BuildConfigSpecMod + success bool + }{ + { + name: "passing one BuildConfig without name", + input: []BuildConfigSpecMod{ + {}, + }, + output: []BuildConfigSpecMod{ + { + ObjectMeta: meta_v1.ObjectMeta{ + Name: appName, + Labels: map[string]string{ + appLabelKey: appName, + }, + }, + }, + }, + success: true, + }, + { + name: "passing one BuildConfig with name", + input: []BuildConfigSpecMod{ + { + ObjectMeta: meta_v1.ObjectMeta{ + Name: "bcName", + }, + }, + }, + output: []BuildConfigSpecMod{ + { + ObjectMeta: meta_v1.ObjectMeta{ + Name: "bcName", + Labels: map[string]string{ + appLabelKey: appName, + }, + }, + }, + }, + success: true, + }, + { + name: "passing multiple BuildConfigs without names", + input: []BuildConfigSpecMod{ + {}, + {}, + }, + output: nil, + success: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fixedBuildConfigs, err := fixBuildConfigs(test.input, appName) + + switch test.success { + case true: + if err != nil { + t.Errorf("Expected test to pass but got an error -\n%v", err) + } + case false: + if err == nil { + t.Errorf("For the input -\n%v\nexpected test to fail, but test passed", prettyPrintObjects(test.input)) + } + } + + if !reflect.DeepEqual(fixedBuildConfigs, test.output) { + t.Errorf("Expected fixed BuildConfigs to be -\n%v\nBut got -\n%v\n", prettyPrintObjects(test.output), prettyPrintObjects(fixedBuildConfigs)) + } + }) + } +} + func TestFixSecrets(t *testing.T) { failingTest := []SecretMod{{}, {}} _, err := fixSecrets(failingTest, "") @@ -796,6 +876,98 @@ func TestCreateImageStreams(t *testing.T) { } } +func TestCreateBuildConfigs(t *testing.T) { + tests := []struct { + name string + input *ControllerFields + output []runtime.Object + }{ + { + name: "no buildConfig passed", + input: &ControllerFields{}, + output: nil, + }, + { + name: "passing 1 buildConfig definition", + input: &ControllerFields{ + BuildConfigs: []BuildConfigSpecMod{ + { + ObjectMeta: meta_v1.ObjectMeta{ + Name: "testBC", + }, + BuildConfigSpec: build_v1.BuildConfigSpec{ + RunPolicy: "Serial", + }, + }, + }, + }, + output: []runtime.Object{ + &build_v1.BuildConfig{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "testBC", + }, + Spec: build_v1.BuildConfigSpec{ + RunPolicy: "Serial", + }, + }, + }, + }, + { + name: "passing 2 buildConfig definitions", + input: &ControllerFields{ + BuildConfigs: []BuildConfigSpecMod{ + { + ObjectMeta: meta_v1.ObjectMeta{ + Name: "testBC1", + }, + BuildConfigSpec: build_v1.BuildConfigSpec{ + RunPolicy: "Serial", + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + Name: "testBC2", + }, + BuildConfigSpec: build_v1.BuildConfigSpec{ + RunPolicy: "Serial", + }, + }, + }, + }, + output: []runtime.Object{ + &build_v1.BuildConfig{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "testBC1", + }, + Spec: build_v1.BuildConfigSpec{ + RunPolicy: "Serial", + }, + }, + &build_v1.BuildConfig{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "testBC2", + }, + Spec: build_v1.BuildConfigSpec{ + RunPolicy: "Serial", + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + objects, err := test.input.createBuildConfigs() + if err != nil { + t.Errorf("Creating buildConfigs failed: %v", err) + } + if !reflect.DeepEqual(test.output, objects) { + t.Fatalf("Expected:\n%v\nGot:\n%v", prettyPrintObjects(test.output), prettyPrintObjects(objects)) + } + }) + } +} + func TestParsePortMapping(t *testing.T) { tests := []struct { name string diff --git a/pkg/spec/types.go b/pkg/spec/types.go index 6889f4175..6c4e1a7a0 100644 --- a/pkg/spec/types.go +++ b/pkg/spec/types.go @@ -17,6 +17,7 @@ limitations under the License. package spec import ( + build_v1 "github.com/openshift/origin/pkg/build/apis/build/v1" os_deploy_v1 "github.com/openshift/origin/pkg/deploy/apis/apps/v1" image_v1 "github.com/openshift/origin/pkg/image/apis/image/v1" os_route_v1 "github.com/openshift/origin/pkg/route/apis/route/v1" @@ -151,6 +152,14 @@ type ImageStreamSpecMod struct { meta_v1.ObjectMeta `json:",inline"` } +// BuildConfigSpecMod defines OpenShift BuildConfig object +// kedgeSpec: io.kedge.BuildConfigSpec +type BuildConfigSpecMod struct { + build_v1.BuildConfigSpec `json:",inline"` + // k8s: io.k8s.kubernetes.pkg.apis.meta.v1.ObjectMeta + meta_v1.ObjectMeta `json:",inline"` +} + // ControllerFields are the common fields in every controller Kedge supports type ControllerFields struct { Controller string `json:"controller,omitempty"` @@ -182,6 +191,10 @@ type ControllerFields struct { // ref: io.kedge.ImageStreamSpec // +optional ImageStreams []ImageStreamSpecMod `json:"imageStreams,omitempty"` + // List of OpenShift BuildConfigs + // ref: io.kedge.BuildConfigSpec + // +optional + BuildConfigs []BuildConfigSpecMod `json:"buildConfigs,omitempty"` // List of Kubernetes resource files, that can be directly given to Kubernetes // +optional IncludeResources []string `json:"includeResources,omitempty"` diff --git a/pkg/spec/util.go b/pkg/spec/util.go index 16ee92d1c..294c54aea 100644 --- a/pkg/spec/util.go +++ b/pkg/spec/util.go @@ -28,6 +28,7 @@ import ( "k8s.io/kubernetes/pkg/api" api_v1 "k8s.io/kubernetes/pkg/api/v1" //kapi "k8s.io/kubernetes/pkg/api/v1" + build_v1 "github.com/openshift/origin/pkg/build/apis/build/v1" os_deploy_v1 "github.com/openshift/origin/pkg/deploy/apis/apps/v1" image_v1 "github.com/openshift/origin/pkg/image/apis/image/v1" os_route_v1 "github.com/openshift/origin/pkg/route/apis/route/v1" @@ -92,6 +93,11 @@ func GetScheme() (*runtime.Scheme, error) { return nil, errors.Wrap(err, "unable to add 'image' to scheme") } + // Adding the build scheme to support OpenShift buildConfigs + if err := build_v1.AddToScheme(scheme); err != nil { + return nil, errors.Wrap(err, "unable to add 'build' to scheme") + } + return scheme, nil }