From 2dbd7ecc14ab382338488d3dc8eb4b2069351a7e Mon Sep 17 00:00:00 2001 From: Jessica Yuen <im.jessicayuen@gmail.com> Date: Wed, 31 Jan 2018 10:46:26 -0800 Subject: [PATCH] Add EnvironmentSpec to app.yaml Signed-off-by: Jessica Yuen <im.jessicayuen@gmail.com> --- metadata/app/schema.go | 125 ++++++++++++++++++++++------- metadata/app/schema_test.go | 152 +++++++++++++++++++++++++++++++++++- metadata/manager.go | 11 +-- 3 files changed, 253 insertions(+), 35 deletions(-) diff --git a/metadata/app/schema.go b/metadata/app/schema.go index c3c720e9..1db429f8 100644 --- a/metadata/app/schema.go +++ b/metadata/app/schema.go @@ -29,8 +29,14 @@ const ( DefaultVersion = "0.0.1" ) -var ErrRegistryNameInvalid = fmt.Errorf("Registry name is invalid") -var ErrRegistryExists = fmt.Errorf("Registry with name already exists") +var ( + ErrRegistryNameInvalid = fmt.Errorf("Registry name is invalid") + ErrRegistryExists = fmt.Errorf("Registry with name already exists") + // ErrEnvironmentNameInvalid is the error where an environment name is invalid. + ErrEnvironmentNameInvalid = fmt.Errorf("Environment name is invalid") + // ErrEnvironmentExists is the error when trying to create an environment that already exists. + ErrEnvironmentExists = fmt.Errorf("Environment with name already exists") +) type Spec struct { APIVersion string `json:"apiVersion,omitempty"` @@ -44,10 +50,76 @@ type Spec struct { Bugs string `json:"bugs,omitempty"` Keywords []string `json:"keywords,omitempty"` Registries RegistryRefSpecs `json:"registries,omitempty"` + Environments EnvironmentSpecs `json:"environments,omitempty"` Libraries LibraryRefSpecs `json:"libraries,omitempty"` License string `json:"license,omitempty"` } +type RepositorySpec struct { + Type string `json:"type"` + URI string `json:"uri"` +} + +type RegistryRefSpec struct { + Name string `json:"-"` + Protocol string `json:"protocol"` + URI string `json:"uri"` + GitVersion *GitVersionSpec `json:"gitVersion"` +} + +type RegistryRefSpecs map[string]*RegistryRefSpec + +// EnvironmentSpecs contains one or more EnvironmentSpec. +type EnvironmentSpecs map[string]*EnvironmentSpec + +// EnvironmentSpec contains the specification for ksonnet environments. +// +// KubernetesVersion: The Kubernetes version the target cluster is running on. +// Destinations: One or more cluster addresses that this environment +// points to. +// Targets: The relative component paths that this environment wishes +// to deploy onto it's destinations. +type EnvironmentSpec struct { + Name string `json:"-"` + KubernetesVersion string `json:"k8sVersion"` + Destinations EnvironmentDestinationSpecs `json:"destinations"` + Targets []string `json:"targets"` +} + +// EnvironmentDestinationSpecs contains one or more EnvironmentDestinationSpec. +type EnvironmentDestinationSpecs []*EnvironmentDestinationSpec + +// EnvironmentDestinationSpec contains the specification for the cluster +// addresses that the environment points to. +// +// Server: The Kubernetes server that the cluster is running on. +// Namespace: The namespace of the Kubernetes server that targets should +// be deployed to. This is "default", by default. +type EnvironmentDestinationSpec struct { + Server string `json:"server"` + Namespace string `json:"namespace"` +} + +type LibraryRefSpec struct { + Name string `json:"name"` + Registry string `json:"registry"` + GitVersion *GitVersionSpec `json:"gitVersion"` +} + +type GitVersionSpec struct { + RefSpec string `json:"refSpec"` + CommitSHA string `json:"commitSha"` +} + +type LibraryRefSpecs map[string]*LibraryRefSpec + +type ContributorSpec struct { + Name string `json:"name"` + Email string `json:"email"` +} + +type ContributorSpecs []*ContributorSpec + func Unmarshal(bytes []byte) (*Spec, error) { schema := Spec{} err := yaml.Unmarshal(bytes, &schema) @@ -105,36 +177,33 @@ func (s *Spec) validate() error { return nil } -type RepositorySpec struct { - Type string `json:"type"` - URI string `json:"uri"` -} - -type RegistryRefSpec struct { - Name string `json:"-"` - Protocol string `json:"protocol"` - URI string `json:"uri"` - GitVersion *GitVersionSpec `json:"gitVersion"` +// GetEnvironmentSpec returns the environment specification for the environment. +func (s *Spec) GetEnvironmentSpec(name string) (*EnvironmentSpec, bool) { + environmentSpec, ok := s.Environments[name] + if ok { + environmentSpec.Name = name + } + return environmentSpec, ok } -type RegistryRefSpecs map[string]*RegistryRefSpec +// AddEnvironmentSpec adds an EnvironmentSpec to the list of EnvironmentSpecs. +// This is equivalent to registering the environment for a ksonnet app. +func (s *Spec) AddEnvironmentSpec(spec *EnvironmentSpec) error { + if spec.Name == "" { + return ErrEnvironmentNameInvalid + } -type LibraryRefSpec struct { - Name string `json:"name"` - Registry string `json:"registry"` - GitVersion *GitVersionSpec `json:"gitVersion"` -} + _, environmentSpecExists := s.Environments[spec.Name] + if environmentSpecExists { + return ErrEnvironmentExists + } -type GitVersionSpec struct { - RefSpec string `json:"refSpec"` - CommitSHA string `json:"commitSha"` + s.Environments[spec.Name] = spec + return nil } -type LibraryRefSpecs map[string]*LibraryRefSpec - -type ContributorSpec struct { - Name string `json:"name"` - Email string `json:"email"` +// DeleteEnvironmentSpec removes the environment specification from the app spec. +func (s *Spec) DeleteEnvironmentSpec(name string) error { + delete(s.Environments, name) + return nil } - -type ContributorSpecs []*ContributorSpec diff --git a/metadata/app/schema_test.go b/metadata/app/schema_test.go index c56112d4..b0036777 100644 --- a/metadata/app/schema_test.go +++ b/metadata/app/schema_test.go @@ -16,7 +16,6 @@ package app import ( - "fmt" "testing" "github.com/blang/semver" @@ -34,6 +33,19 @@ func makeSimpleRefSpec(name, protocol, uri, version string) *RegistryRefSpec { } } +func makeSimpleEnvironmentSpec(name, namespace, server, k8sVersion string) *EnvironmentSpec { + return &EnvironmentSpec{ + Name: name, + Destinations: EnvironmentDestinationSpecs{ + &EnvironmentDestinationSpec{ + Namespace: namespace, + Server: server, + }, + }, + KubernetesVersion: k8sVersion, + } +} + func TestApiVersionValidate(t *testing.T) { type spec struct { spec string @@ -87,7 +99,6 @@ func TestGetRegistryRefSuccess(t *testing.T) { } r1, ok := example1.GetRegistryRef("simple1") - fmt.Println(r1) if r1 == nil || !ok { t.Error("Expected registry to contain 'simple1'") } @@ -159,3 +170,140 @@ func TestAddRegistryRefFailure(t *testing.T) { t.Error("Expected registry to fail to add registry with duplicate name and different uri") } } + +func TestGetEnvironmentSpecSuccess(t *testing.T) { + const ( + env = "dev" + namespace = "default" + server = "http://example.com" + k8sVersion = "1.8.0" + ) + + example1 := Spec{ + Environments: EnvironmentSpecs{ + env: &EnvironmentSpec{ + Destinations: EnvironmentDestinationSpecs{ + &EnvironmentDestinationSpec{ + Namespace: namespace, + Server: server, + }, + }, + KubernetesVersion: k8sVersion, + }, + }, + } + + r1, ok := example1.GetEnvironmentSpec(env) + if r1 == nil || !ok { + t.Errorf("Expected environments to contain '%s'", env) + } + + if len(r1.Destinations) != 1 || r1.Destinations[0].Namespace != namespace || + r1.Destinations[0].Server != server || r1.KubernetesVersion != k8sVersion { + t.Errorf("Environment did not add correct values:\n%s", r1) + } +} + +func TestGetEnvironmentSpecFailure(t *testing.T) { + example1 := Spec{ + Environments: EnvironmentSpecs{ + "dev": &EnvironmentSpec{ + Destinations: EnvironmentDestinationSpecs{ + &EnvironmentDestinationSpec{ + Namespace: "default", + Server: "http://example.com", + }, + }, + KubernetesVersion: "1.8.0", + }, + }, + } + + r1, ok := example1.GetEnvironmentSpec("prod") + if r1 != nil || ok { + t.Error("Expected environemnts to not contain 'prod'") + } +} + +func TestAddEnvironmentSpecSuccess(t *testing.T) { + const ( + env = "dev" + namespace = "default" + server = "http://example.com" + k8sVersion = "1.8.0" + ) + + example1 := Spec{ + Environments: EnvironmentSpecs{}, + } + + err := example1.AddEnvironmentSpec(makeSimpleEnvironmentSpec(env, namespace, server, k8sVersion)) + if err != nil { + t.Errorf("Expected environment add to succeed:\n%s", err) + } + + r1, ok1 := example1.GetEnvironmentSpec(env) + if !ok1 || len(r1.Destinations) != 1 || r1.Destinations[0].Namespace != namespace || + r1.Destinations[0].Server != server || r1.KubernetesVersion != k8sVersion { + t.Errorf("Environment did not add correct values:\n%s", r1) + } +} + +func TestAddEnvironmentSpecFailure(t *testing.T) { + const ( + envName1 = "dev" + envName2 = "" + namespace = "default" + server = "http://example.com" + k8sVersion = "1.8.0" + ) + + example1 := Spec{ + Environments: EnvironmentSpecs{ + envName1: &EnvironmentSpec{ + Destinations: EnvironmentDestinationSpecs{ + &EnvironmentDestinationSpec{ + Namespace: namespace, + Server: server, + }, + }, + KubernetesVersion: k8sVersion, + }, + }, + } + + err := example1.AddEnvironmentSpec(makeSimpleEnvironmentSpec(envName2, namespace, server, k8sVersion)) + if err != ErrEnvironmentNameInvalid { + t.Error("Expected failure while adding environment with an invalid name") + } + + err = example1.AddEnvironmentSpec(makeSimpleEnvironmentSpec(envName1, namespace, server, k8sVersion)) + if err != ErrEnvironmentExists { + t.Error("Expected failure while adding environment with duplicate name") + } +} + +func TestDeleteEnvironmentSpec(t *testing.T) { + example1 := Spec{ + Environments: EnvironmentSpecs{ + "dev": &EnvironmentSpec{ + Destinations: EnvironmentDestinationSpecs{ + &EnvironmentDestinationSpec{ + Namespace: "default", + Server: "http://example.com", + }, + }, + KubernetesVersion: "1.8.0", + }, + }, + } + + err := example1.DeleteEnvironmentSpec("dev") + if err != nil { + t.Error("Expected to successfully delete an environment in spec") + } + + if _, ok := example1.GetEnvironmentSpec("dev"); ok { + t.Error("Expected environment 'dev' to be deleted from spec, but still exists") + } +} diff --git a/metadata/manager.go b/metadata/manager.go index 19128b3c..ac400a85 100644 --- a/metadata/manager.go +++ b/metadata/manager.go @@ -395,11 +395,12 @@ func generateRegistryYAMLData(incubatorReg registry.Manager) ([]byte, error) { func generateAppYAMLData(name string, refs ...*app.RegistryRefSpec) ([]byte, error) { content := app.Spec{ - APIVersion: app.DefaultAPIVersion, - Kind: app.Kind, - Name: name, - Version: app.DefaultVersion, - Registries: app.RegistryRefSpecs{}, + APIVersion: app.DefaultAPIVersion, + Kind: app.Kind, + Name: name, + Version: app.DefaultVersion, + Registries: app.RegistryRefSpecs{}, + Environments: app.EnvironmentSpecs{}, } for _, ref := range refs { -- GitLab