diff --git a/pkg/client/install.go b/pkg/client/install.go index 59e53c367d176b56d01c4949628188877ae967d5..8a582f2d1619e11df2b8628e9615ac475e37b91c 100644 --- a/pkg/client/install.go +++ b/pkg/client/install.go @@ -43,7 +43,7 @@ func (i *Installer) Install() error { return err } - return kube.Create("helm", &b, nil) + return kube.New(nil).Create("helm", &b) } // InstallYAML is the installation YAML for DM. diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 7f6acf02977f102d50828b20c58db71ee98af7ea..0fa72473dede1462b5ff6a8ed2cbfbb7883bbb47 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -9,7 +9,17 @@ import ( "k8s.io/kubernetes/pkg/kubectl/resource" ) -const includeThirdPartyAPIs = false +// Client represents a client capable of communicating with the Kubernetes API. +type Client struct { + config clientcmd.ClientConfig +} + +// New create a new Client +func New(config clientcmd.ClientConfig) *Client { + return &Client{ + config: config, + } +} // ResourceActorFunc performs an action on a signle resource. type ResourceActorFunc func(*resource.Info) error @@ -17,12 +27,13 @@ type ResourceActorFunc func(*resource.Info) error // Create creates kubernetes resources from an io.reader // // Namespace will set the namespace -// Config allows for overiding values from kubectl -func Create(namespace string, reader io.Reader, config clientcmd.ClientConfig) error { - f := cmdutil.NewFactory(config) +func (c *Client) Create(namespace string, reader io.Reader) error { + f := cmdutil.NewFactory(c.config) return perform(f, namespace, reader, createResource) } +const includeThirdPartyAPIs = false + func perform(f *cmdutil.Factory, namespace string, reader io.Reader, fn ResourceActorFunc) error { r := f.NewBuilder(includeThirdPartyAPIs). ContinueOnError(). diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index daeb3dee4ed4c6d0b47eafddfcd8c801899b2485..a13d4c1ab1a3904cfd5300c9f588918237a21c7f 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -1,7 +1,8 @@ package kube import ( - "os" + "io" + "strings" "testing" "k8s.io/kubernetes/pkg/api/meta" @@ -11,34 +12,179 @@ import ( ) func TestPerform(t *testing.T) { - input, err := os.Open("./testdata/guestbook-all-in-one.yaml") - if err != nil { - t.Fatal(err) + tests := []struct { + name string + namespace string + reader io.Reader + count int + err bool + errMessage string + }{ + { + name: "Valid input", + namespace: "test", + reader: strings.NewReader(guestbookManifest), + count: 6, + }, { + name: "Empty manifests", + namespace: "test", + reader: strings.NewReader(""), + err: true, + errMessage: "no objects passed to create", + }, } - defer input.Close() - results := []*resource.Info{} + for _, tt := range tests { + results := []*resource.Info{} - fn := func(info *resource.Info) error { - results = append(results, info) + fn := func(info *resource.Info) error { + results = append(results, info) - if info.Namespace != "test" { - t.Errorf("expected namespace to be 'test', got %s", info.Namespace) + if info.Namespace != tt.namespace { + t.Errorf("%q. expected namespace to be '%s', got %s", tt.name, tt.namespace, info.Namespace) + } + return nil } - return nil - } - - f := cmdutil.NewFactory(nil) - f.ClientForMapping = func(mapping *meta.RESTMapping) (resource.RESTClient, error) { - return &fake.RESTClient{}, nil - } + f := cmdutil.NewFactory(nil) + f.ClientForMapping = func(mapping *meta.RESTMapping) (resource.RESTClient, error) { + return &fake.RESTClient{}, nil + } - if err := perform(f, "test", input, fn); err != nil { - t.Fatalf("Unexpected error: %s", err) - } + err := perform(f, tt.namespace, tt.reader, fn) + if (err != nil) != tt.err { + t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) + } + if err != nil && err.Error() != tt.errMessage { + t.Errorf("%q. expected error message: %v, got %v", tt.name, tt.errMessage, err) + } - if len(results) != 6 { - t.Errorf("expected 6 result objects, got %d", len(results)) + if len(results) != tt.count { + t.Errorf("%q. expected %d result objects, got %d", tt.name, tt.count, len(results)) + } } } + +const guestbookManifest = ` +apiVersion: v1 +kind: Service +metadata: + name: redis-master + labels: + app: redis + tier: backend + role: master +spec: + ports: + - port: 6379 + targetPort: 6379 + selector: + app: redis + tier: backend + role: master +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: redis-master +spec: + replicas: 1 + template: + metadata: + labels: + app: redis + role: master + tier: backend + spec: + containers: + - name: master + image: gcr.io/google_containers/redis:e2e # or just image: redis + resources: + requests: + cpu: 100m + memory: 100Mi + ports: + - containerPort: 6379 +--- +apiVersion: v1 +kind: Service +metadata: + name: redis-slave + labels: + app: redis + tier: backend + role: slave +spec: + ports: + # the port that this service should serve on + - port: 6379 + selector: + app: redis + tier: backend + role: slave +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: redis-slave +spec: + replicas: 2 + template: + metadata: + labels: + app: redis + role: slave + tier: backend + spec: + containers: + - name: slave + image: gcr.io/google_samples/gb-redisslave:v1 + resources: + requests: + cpu: 100m + memory: 100Mi + env: + - name: GET_HOSTS_FROM + value: dns + ports: + - containerPort: 6379 +--- +apiVersion: v1 +kind: Service +metadata: + name: frontend + labels: + app: guestbook + tier: frontend +spec: + ports: + - port: 80 + selector: + app: guestbook + tier: frontend +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: frontend +spec: + replicas: 3 + template: + metadata: + labels: + app: guestbook + tier: frontend + spec: + containers: + - name: php-redis + image: gcr.io/google-samples/gb-frontend:v4 + resources: + requests: + cpu: 100m + memory: 100Mi + env: + - name: GET_HOSTS_FROM + value: dns + ports: + - containerPort: 80 +` diff --git a/pkg/kube/testdata/guestbook-all-in-one.yaml b/pkg/kube/testdata/guestbook-all-in-one.yaml deleted file mode 100644 index c6675e0eb673a954d791d0684fdcf09a89bc435b..0000000000000000000000000000000000000000 --- a/pkg/kube/testdata/guestbook-all-in-one.yaml +++ /dev/null @@ -1,179 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: redis-master - labels: - app: redis - tier: backend - role: master -spec: - ports: - # the port that this service should serve on - - port: 6379 - targetPort: 6379 - selector: - app: redis - tier: backend - role: master ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: redis-master - # these labels can be applied automatically - # from the labels in the pod template if not set - # labels: - # app: redis - # role: master - # tier: backend -spec: - # this replicas value is default - # modify it according to your case - replicas: 1 - # selector can be applied automatically - # from the labels in the pod template if not set - # selector: - # matchLabels: - # app: guestbook - # role: master - # tier: backend - template: - metadata: - labels: - app: redis - role: master - tier: backend - spec: - containers: - - name: master - image: gcr.io/google_containers/redis:e2e # or just image: redis - resources: - requests: - cpu: 100m - memory: 100Mi - ports: - - containerPort: 6379 ---- -apiVersion: v1 -kind: Service -metadata: - name: redis-slave - labels: - app: redis - tier: backend - role: slave -spec: - ports: - # the port that this service should serve on - - port: 6379 - selector: - app: redis - tier: backend - role: slave ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: redis-slave - # these labels can be applied automatically - # from the labels in the pod template if not set - # labels: - # app: redis - # role: slave - # tier: backend -spec: - # this replicas value is default - # modify it according to your case - replicas: 2 - # selector can be applied automatically - # from the labels in the pod template if not set - # selector: - # matchLabels: - # app: guestbook - # role: slave - # tier: backend - template: - metadata: - labels: - app: redis - role: slave - tier: backend - spec: - containers: - - name: slave - image: gcr.io/google_samples/gb-redisslave:v1 - resources: - requests: - cpu: 100m - memory: 100Mi - env: - - name: GET_HOSTS_FROM - value: dns - # If your cluster config does not include a dns service, then to - # instead access an environment variable to find the master - # service's host, comment out the 'value: dns' line above, and - # uncomment the line below. - # value: env - ports: - - containerPort: 6379 ---- -apiVersion: v1 -kind: Service -metadata: - name: frontend - labels: - app: guestbook - tier: frontend -spec: - # if your cluster supports it, uncomment the following to automatically create - # an external load-balanced IP for the frontend service. - # type: LoadBalancer - ports: - # the port that this service should serve on - - port: 80 - selector: - app: guestbook - tier: frontend ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: frontend - # these labels can be applied automatically - # from the labels in the pod template if not set - # labels: - # app: guestbook - # tier: frontend -spec: - # this replicas value is default - # modify it according to your case - replicas: 3 - # selector can be applied automatically - # from the labels in the pod template if not set - # selector: - # matchLabels: - # app: guestbook - # tier: frontend - template: - metadata: - labels: - app: guestbook - tier: frontend - spec: - containers: - - name: php-redis - image: gcr.io/google-samples/gb-frontend:v4 - resources: - requests: - cpu: 100m - memory: 100Mi - env: - - name: GET_HOSTS_FROM - value: dns - # If your cluster config does not include a dns service, then to - # instead access environment variables to find service host - # info, comment out the 'value: dns' line above, and uncomment the - # line below. - # value: env - ports: - - containerPort: 80