From 4e0b163e08a28250facf21555bc8f72c9f58fc28 Mon Sep 17 00:00:00 2001
From: Jessica Yuen <im.jessicayuen@gmail.com>
Date: Thu, 14 Dec 2017 15:59:09 -0800
Subject: [PATCH] Implement command `ks registry add`

Currently users are unable to add their own registries through the CLI.
This limits them to a small subset of prototypes found in the default
incubator registry.

This commit will add the command `ks registry add`, that allows users to
add registries supporting the `github` protocol.

It will be of the form `ks registry add <registry-name> <registry-uri>
[--version]`. If a version is not specified, `latest` will be used.

Signed-off-by: Jessica Yuen <im.jessicayuen@gmail.com>
---
 cmd/registry.go                       | 61 +++++++++++++++++++++++++++
 docs/cli-reference/ks_registry.md     |  1 +
 docs/cli-reference/ks_registry_add.md | 59 ++++++++++++++++++++++++++
 metadata/registry.go                  | 46 ++++++++++----------
 pkg/kubecfg/registry.go               | 44 +++++++++++++++++++
 5 files changed, 187 insertions(+), 24 deletions(-)
 create mode 100644 docs/cli-reference/ks_registry_add.md
 create mode 100644 pkg/kubecfg/registry.go

diff --git a/cmd/registry.go b/cmd/registry.go
index 6f6b8500..a2a77194 100644
--- a/cmd/registry.go
+++ b/cmd/registry.go
@@ -6,19 +6,28 @@ import (
 	"strings"
 
 	"github.com/ksonnet/ksonnet/metadata"
+	"github.com/ksonnet/ksonnet/pkg/kubecfg"
 	"github.com/ksonnet/ksonnet/utils"
 	"github.com/spf13/cobra"
 )
 
+const (
+	flagRegistryVersion = "version"
+)
+
 var regShortDesc = map[string]string{
 	"list":     "List all registries known to the current ksonnet app.",
 	"describe": "Describe a ksonnet registry and the packages it contains",
+	"add":      "Add a registry to the current ksonnet app",
 }
 
 func init() {
 	RootCmd.AddCommand(registryCmd)
 	registryCmd.AddCommand(registryListCmd)
 	registryCmd.AddCommand(registryDescribeCmd)
+	registryCmd.AddCommand(registryAddCmd)
+
+	registryAddCmd.PersistentFlags().String(flagRegistryVersion, "", "Version of the registry to add")
 }
 
 var registryCmd = &cobra.Command{
@@ -182,3 +191,55 @@ by ` + "`<registry-name>`" + `. Specifically, it displays the following:
 ### Syntax
 `,
 }
+
+var registryAddCmd = &cobra.Command{
+	Use:   "add <registry-name> <registry-uri>",
+	Short: regShortDesc["add"],
+	RunE: func(cmd *cobra.Command, args []string) error {
+		flags := cmd.Flags()
+
+		if len(args) != 2 {
+			return fmt.Errorf("Command 'registry add' takes two arguments, which is the name and the repository address of the registry to add")
+		}
+
+		name := args[0]
+		uri := args[1]
+
+		version, err := flags.GetString(flagRegistryVersion)
+		if err != nil {
+			return err
+		}
+
+		// TODO allow protocol to be specified by flag once there is greater
+		// support for other protocol types.
+		return kubecfg.NewRegistryAddCmd(name, "github", uri, version).Run()
+	},
+
+	Long: `
+The ` + "`add`" + ` command allows custom registries to be added to your ksonnet app.
+
+A registry is uniquely identified by its:
+
+1. Name
+2. Version
+
+Currently, only registries supporting the GitHub protocol can be added.
+
+All registries must specify a unique name and URI where the registry lives.
+Optionally, a version can be provided. If a version is not specified, it will
+default to  ` + "`latest`" + `.
+
+
+### Related Commands
+
+* ` + "`ks registry list` " + `— ` + regShortDesc["list"] + `
+
+### Syntax
+`,
+	Example: `# Add a registry with the name 'databases' at the uri 'github.com/example'
+ks registry add databases github.com/example
+
+# Add a registry with the name 'databases' at the uri 'github.com/example' and
+# the version 0.0.1
+ks registry add databases github.com/example --version=0.0.1`,
+}
diff --git a/docs/cli-reference/ks_registry.md b/docs/cli-reference/ks_registry.md
index 1cc2cba2..b0b86bc3 100644
--- a/docs/cli-reference/ks_registry.md
+++ b/docs/cli-reference/ks_registry.md
@@ -35,6 +35,7 @@ ks registry
 
 ### SEE ALSO
 * [ks](ks.md)	 - Configure your application to deploy to a Kubernetes cluster
+* [ks registry add](ks_registry_add.md)	 - Add a registry to the current ksonnet app
 * [ks registry describe](ks_registry_describe.md)	 - Describe a ksonnet registry and the packages it contains
 * [ks registry list](ks_registry_list.md)	 - List all registries known to the current ksonnet app.
 
diff --git a/docs/cli-reference/ks_registry_add.md b/docs/cli-reference/ks_registry_add.md
new file mode 100644
index 00000000..636be31b
--- /dev/null
+++ b/docs/cli-reference/ks_registry_add.md
@@ -0,0 +1,59 @@
+## ks registry add
+
+Add a registry to the current ksonnet app
+
+### Synopsis
+
+
+
+The `add` command allows custom registries to be added to your ksonnet app.
+
+A registry is uniquely identified by its:
+
+1. Name
+2. Version
+
+Currently, only registries supporting the GitHub protocol can be added.
+
+All registries must specify a unique name and URI where the registry lives.
+Optionally, a version can be provided. If a version is not specified, it will
+default to  `latest`.
+
+
+### Related Commands
+
+* `ks registry list` — List all registries known to the current ksonnet app.
+
+### Syntax
+
+
+```
+ks registry add <registry-name> <registry-uri>
+```
+
+### Examples
+
+```
+# Add a registry with the name 'databases' at the uri 'github.com/example'
+ks registry add databases github.com/example
+
+# Add a registry with the name 'databases' at the uri 'github.com/example' and
+# the version 0.0.1
+ks registry add databases github.com/example --version=0.0.1
+```
+
+### Options
+
+```
+      --version string   Version of the registry to add
+```
+
+### Options inherited from parent commands
+
+```
+  -v, --verbose count[=-1]   Increase verbosity. May be given multiple times.
+```
+
+### SEE ALSO
+* [ks registry](ks_registry.md)	 - Manage registries for current project
+
diff --git a/metadata/registry.go b/metadata/registry.go
index 24fb22e9..38b6b171 100644
--- a/metadata/registry.go
+++ b/metadata/registry.go
@@ -351,33 +351,31 @@ func (m *manager) getOrCacheRegistry(gh registry.Manager) (*registry.Spec, error
 	registrySpecFile := m.registryPath(gh)
 	registrySpec, exists, err := m.registrySpecFromFile(registrySpecFile)
 	if !exists {
-		return nil, fmt.Errorf("Registry '%s' does not exist", gh.MakeRegistryRefSpec().Name)
-	} else if err != nil {
-		return nil, err
-	}
-
-	// If failed, use the protocol to try to retrieve app specification.
-	registrySpec, err = gh.FetchRegistrySpec()
-	if err != nil {
-		return nil, err
-	}
+		// If failed, use the protocol to try to retrieve app specification.
+		registrySpec, err = gh.FetchRegistrySpec()
+		if err != nil {
+			return nil, err
+		}
 
-	registrySpecBytes, err := registrySpec.Marshal()
-	if err != nil {
-		return nil, err
-	}
+		registrySpecBytes, err := registrySpec.Marshal()
+		if err != nil {
+			return nil, err
+		}
 
-	// NOTE: We call mkdir after getting the registry spec, since a
-	// network call might fail and leave this half-initialized empty
-	// directory.
-	registrySpecDir := appendToAbsPath(m.registriesPath, gh.RegistrySpecDir())
-	err = m.appFS.MkdirAll(string(registrySpecDir), defaultFolderPermissions)
-	if err != nil {
-		return nil, err
-	}
+		// NOTE: We call mkdir after getting the registry spec, since a
+		// network call might fail and leave this half-initialized empty
+		// directory.
+		registrySpecDir := appendToAbsPath(m.registriesPath, gh.RegistrySpecDir())
+		err = m.appFS.MkdirAll(string(registrySpecDir), defaultFolderPermissions)
+		if err != nil {
+			return nil, err
+		}
 
-	err = afero.WriteFile(m.appFS, string(registrySpecFile), registrySpecBytes, defaultFilePermissions)
-	if err != nil {
+		err = afero.WriteFile(m.appFS, string(registrySpecFile), registrySpecBytes, defaultFilePermissions)
+		if err != nil {
+			return nil, err
+		}
+	} else if err != nil {
 		return nil, err
 	}
 
diff --git a/pkg/kubecfg/registry.go b/pkg/kubecfg/registry.go
new file mode 100644
index 00000000..4de55f78
--- /dev/null
+++ b/pkg/kubecfg/registry.go
@@ -0,0 +1,44 @@
+// Copyright 2017 The ksonnet 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 kubecfg
+
+// RegistryAddCmd contains the metadata needed to create a registry.
+type RegistryAddCmd struct {
+	name     string
+	protocol string
+	uri      string
+	version  string
+}
+
+// NewRegistryAddCmd initializes a RegistryAddCmd.
+func NewRegistryAddCmd(name, protocol, uri, version string) *RegistryAddCmd {
+	if version == "" {
+		version = "latest"
+	}
+
+	return &RegistryAddCmd{name: name, protocol: protocol, uri: uri, version: version}
+}
+
+// Run adds the registry to the ksonnet project.
+func (c *RegistryAddCmd) Run() error {
+	manager, err := manager()
+	if err != nil {
+		return err
+	}
+
+	_, err = manager.AddRegistry(c.name, c.protocol, c.uri, c.version)
+	return err
+}
-- 
GitLab