From 5ddc85403cac324f6101374c3d9b43f835ab1bbc Mon Sep 17 00:00:00 2001 From: Alex Clemmer <clemmer.alexander@gmail.com> Date: Mon, 30 Oct 2017 01:14:02 -0700 Subject: [PATCH] Add schemas for `app.yaml` and `registry.yaml` Every ksonnet application has an `app.yaml`, which is similar in principle to node.js's `package.json`. It contains important metadata, such as description, names, and information about dependencies. This is similarly true of `registry.yaml`, which contains information about a package registry. This commit will introduce schemas for both these files. --- metadata/app/schema.go | 106 ++++++++++++++++++++++++++++++++++++ metadata/app/schema_test.go | 101 ++++++++++++++++++++++++++++++++++ metadata/registry/schema.go | 34 ++++++++++++ 3 files changed, 241 insertions(+) create mode 100644 metadata/app/schema.go create mode 100644 metadata/app/schema_test.go create mode 100644 metadata/registry/schema.go diff --git a/metadata/app/schema.go b/metadata/app/schema.go new file mode 100644 index 00000000..4d1d3ac5 --- /dev/null +++ b/metadata/app/schema.go @@ -0,0 +1,106 @@ +// Copyright 2017 The kubecfg authors +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "encoding/json" + "fmt" +) + +const ( + DefaultAPIVersion = "0.1" + Kind = "ksonnet.io/app" + DefaultVersion = "0.0.1" +) + +var ErrRegistryNameInvalid = fmt.Errorf("Registry name is invalid") +var ErrRegistryExists = fmt.Errorf("Registry with name already exists, but has different URL or protocol") + +type Spec struct { + APIVersion string `json:"apiVersion,omitempty"` + Kind string `json:"kind,omitempty"` + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` + Description string `json:"description,omitempty"` + Authors []string `json:"authors,omitempty"` + Contributors ContributorSpecs `json:"contributors,omitempty"` + Repository *RepositorySpec `json:"repository,omitempty"` + Bugs string `json:"bugs,omitempty"` + Keywords []string `json:"keywords,omitempty"` + Registries RegistryRefSpecs `json:"registries,omitempty"` + License string `json:"license,omitempty"` +} + +func (s *Spec) Marshal() ([]byte, error) { + return json.MarshalIndent(s, "", " ") +} + +func (s *Spec) GetRegistryRef(name string) (*RegistryRefSpec, bool) { + registryRefSpec, ok := s.Registries[name] + if ok { + // Populate name, which we do not include in the deserialization + // process. + registryRefSpec.Name = name + return registryRefSpec, true + } + + return nil, false +} + +func (s *Spec) AddRegistryRef(name, protocol, uri string) (*RegistryRefSpec, error) { + if name == "" { + return nil, ErrRegistryNameInvalid + } + + registryRefSpec, registryRefExists := s.Registries[name] + if registryRefExists { + storedURI, uriExists := registryRefSpec.Spec["uri"] + if registryRefSpec.Protocol != protocol || !uriExists || storedURI != uri { + return nil, ErrRegistryExists + } + } else { + registryRefSpec = &RegistryRefSpec{ + Protocol: protocol, + Spec: map[string]interface{}{ + "uri": uri, + }, + Name: name, + } + } + + s.Registries[name] = registryRefSpec + return registryRefSpec, nil +} + +type RepositorySpec struct { + Type string `json:"type"` + URI string `json:"uri"` +} + +type RegistryRefSpec struct { + Protocol string `json:"protocol"` + Spec map[string]interface{} `json:"spec"` + Name string +} + +type RegistryRefSpecs map[string]*RegistryRefSpec + +type ContributorSpec struct { + Name string `json:"name"` + Email string `json:"email"` +} + +type ContributorSpecs []*ContributorSpec diff --git a/metadata/app/schema_test.go b/metadata/app/schema_test.go new file mode 100644 index 00000000..16e2c20e --- /dev/null +++ b/metadata/app/schema_test.go @@ -0,0 +1,101 @@ +package app + +import ( + "testing" +) + +func TestGetRegistryRefSuccess(t *testing.T) { + example1 := Spec{ + Registries: RegistryRefSpecs{ + "simple1": &RegistryRefSpec{ + Spec: map[string]interface{}{ + "uri": "example.com", + }, + Protocol: "github", + }, + }, + } + + r1, ok := example1.GetRegistryRef("simple1") + if r1 == nil || !ok { + t.Error("Expected registry to contain 'simple1'") + } + + uri, ok := r1.Spec["uri"] + if !ok || uri.(string) != "example.com" || r1.Name != "simple1" || r1.Protocol != "github" { + t.Errorf("Registry did not add correct values:\n%s", r1) + } +} + +func TestGetRegistryRefFailure(t *testing.T) { + example1 := Spec{ + Registries: RegistryRefSpecs{ + "simple1": &RegistryRefSpec{ + Spec: map[string]interface{}{ + "uri": "example.com", + }, + Protocol: "github", + }, + }, + } + + r1, ok := example1.GetRegistryRef("simple2") + if r1 != nil || ok { + t.Error("Expected registry to not contain 'simple2'") + } +} + +func TestAddRegistryRefSuccess(t *testing.T) { + var example1 = Spec{ + Registries: RegistryRefSpecs{}, + } + + r1, err := example1.AddRegistryRef("simple1", "github", "example.com") + if r1 == nil || err != nil { + t.Errorf("Expected registry add to succeed:\n%s", err) + } + + uri1, ok1 := r1.Spec["uri"] + if !ok1 || uri1.(string) != "example.com" || r1.Name != "simple1" || r1.Protocol != "github" { + t.Errorf("Registry did not add correct values:\n%s", r1) + } + + // Test that the `Name` field is added properly if it already exists. + r2, err := example1.AddRegistryRef("simple1", "github", "example.com") + if r2 == nil || err != nil { + t.Errorf("Expected registry add to succeed:\n%s", err) + } + + uri2, ok2 := r2.Spec["uri"] + if !ok2 || uri2.(string) != "example.com" || r1.Name != "simple1" || r1.Protocol != "github" { + t.Errorf("Registry did not add correct values:\n%s", r1) + } +} + +func TestAddRegistryRefFailure(t *testing.T) { + example1 := Spec{ + Registries: RegistryRefSpecs{ + "simple1": &RegistryRefSpec{ + Spec: map[string]interface{}{ + "uri": "example.com", + }, + Protocol: "github", + }, + }, + } + + r1, err := example1.AddRegistryRef("", "github", "example.com") + if r1 != nil || err != ErrRegistryNameInvalid { + t.Error("Expected registry to fail to add registry with invalid name") + } + + r2, err := example1.AddRegistryRef("simple1", "fakeProtocol", "example.com") + if r2 != nil || err != ErrRegistryExists { + t.Error("Expected registry to fail to add registry with duplicate name and different protocol") + } + + r3, err := example1.AddRegistryRef("simple1", "github", "fakeUrl") + if r3 != nil || err != ErrRegistryExists { + t.Error("Expected registry to fail to add registry with duplicate name and different uri") + } +} diff --git a/metadata/registry/schema.go b/metadata/registry/schema.go new file mode 100644 index 00000000..e63d203a --- /dev/null +++ b/metadata/registry/schema.go @@ -0,0 +1,34 @@ +// Copyright 2017 The kubecfg authors +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package registry + +const ( + DefaultApiVersion = "0.1" + DefaultKind = "ksonnet.io/registry" +) + +type Spec struct { + APIVersion string `json:"apiVersion"` + Kind string `json:"kind"` + Prototypes PrototypeRefSpecs `json:"prototypes"` + Libraries LibraryRefSpecs `json:"libraries"` +} + +type Specs []*Spec + +type LibraryRefSpecs map[string]string + +type PrototypeRefSpecs map[string]string -- GitLab