Skip to content
Snippets Groups Projects
Unverified Commit c4ee18ba authored by Bryan Liles's avatar Bryan Liles Committed by GitHub
Browse files

Merge pull request #379 from bryanl/refactor-registry-manager

Create file system based part
parents 7f63e461 f9bb7d62
No related branches found
No related tags found
No related merge requests found
Showing
with 842 additions and 246 deletions
......@@ -22,6 +22,7 @@ import (
cmocks "github.com/ksonnet/ksonnet/component/mocks"
"github.com/ksonnet/ksonnet/metadata/app/mocks"
"github.com/ksonnet/ksonnet/pkg/registry"
rmocks "github.com/ksonnet/ksonnet/pkg/registry/mocks"
"github.com/spf13/afero"
"github.com/stretchr/testify/require"
......@@ -68,7 +69,7 @@ func mockNsWithName(name string) *cmocks.Namespace {
func mockRegistry(name string) *rmocks.Registry {
m := &rmocks.Registry{}
m.On("Name").Return(name)
m.On("Protocol").Return("github")
m.On("Protocol").Return(registry.ProtocolGitHub)
m.On("URI").Return("github.com/ksonnet/parts/tree/master/incubator")
return m
......
// Copyright 2018 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 actions
import (
"github.com/ksonnet/ksonnet/metadata/app"
"github.com/ksonnet/ksonnet/pkg/pkg"
"github.com/ksonnet/ksonnet/pkg/registry"
)
// DepCacher is a function that caches a dependency.j
type DepCacher func(app.App, pkg.Descriptor, string) error
// PkgInstallDepCacher sets the dep cacher for pkg install.
func PkgInstallDepCacher(dc DepCacher) PkgInstallOpt {
return func(pi *PkgInstall) {
pi.depCacher = dc
}
}
// PkgInstallOpt is an option for configuring PkgInstall.
type PkgInstallOpt func(*PkgInstall)
// RunPkgInstall runs `pkg install`
func RunPkgInstall(ksApp app.App, libName, customName string, opts ...PkgInstallOpt) error {
pi, err := NewPkgInstall(ksApp, libName, customName, opts...)
if err != nil {
return err
}
return pi.Run()
}
// PkgInstall lists namespaces.
type PkgInstall struct {
app app.App
libName string
customName string
depCacher DepCacher
}
// NewPkgInstall creates an instance of PkgInstall.
func NewPkgInstall(ksApp app.App, libName, name string, opts ...PkgInstallOpt) (*PkgInstall, error) {
nl := &PkgInstall{
app: ksApp,
libName: libName,
customName: name,
depCacher: registry.CacheDependency,
}
for _, opt := range opts {
opt(nl)
}
return nl, nil
}
// Run lists namespaces.
func (pi *PkgInstall) Run() error {
d, customName, err := pi.parseDepSpec()
if err != nil {
return err
}
return pi.depCacher(pi.app, d, customName)
}
func (pi *PkgInstall) parseDepSpec() (pkg.Descriptor, string, error) {
d, err := pkg.ParseName(pi.libName)
if err != nil {
return pkg.Descriptor{}, "", err
}
customName := pi.customName
if customName == "" {
customName = d.Part
}
return d, customName, nil
}
// Copyright 2018 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 actions
import (
"testing"
"github.com/ksonnet/ksonnet/metadata/app"
amocks "github.com/ksonnet/ksonnet/metadata/app/mocks"
"github.com/ksonnet/ksonnet/pkg/pkg"
"github.com/ksonnet/ksonnet/pkg/registry"
"github.com/stretchr/testify/require"
)
func TestPkgInstall(t *testing.T) {
withApp(t, func(appMock *amocks.App) {
libName := "incubator/apache"
customName := "customName"
dc := func(a app.App, d pkg.Descriptor, cn string) error {
expectedD := pkg.Descriptor{
Registry: "incubator",
Part: "apache",
}
require.Equal(t, expectedD, d)
require.Equal(t, "customName", cn)
return nil
}
dcOpt := PkgInstallDepCacher(dc)
a, err := NewPkgInstall(appMock, libName, customName, dcOpt)
require.NoError(t, err)
libaries := app.LibraryRefSpecs{}
appMock.On("Libraries").Return(libaries, nil)
registries := app.RegistryRefSpecs{
"incubator": &app.RegistryRefSpec{
Protocol: registry.ProtocolFilesystem,
URI: "file:///tmp",
},
}
appMock.On("Registries").Return(registries, nil)
err = a.Run()
require.NoError(t, err)
})
}
// Copyright 2018 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 actions
import (
"io"
"os"
"sort"
"strings"
"github.com/ksonnet/ksonnet/metadata/app"
"github.com/ksonnet/ksonnet/pkg/registry"
"github.com/ksonnet/ksonnet/pkg/util/table"
)
const (
// pkgInstalled denotes a package is installed
pkgInstalled = "*"
)
// RunPkgList runs `pkg list`
func RunPkgList(ksApp app.App) error {
rl, err := NewPkgList(ksApp)
if err != nil {
return err
}
return rl.Run()
}
// PkgList lists available registries
type PkgList struct {
app app.App
rm registry.Manager
out io.Writer
}
// NewPkgList creates an instance of PkgList
func NewPkgList(ksApp app.App) (*PkgList, error) {
rl := &PkgList{
app: ksApp,
rm: registry.DefaultManager,
out: os.Stdout,
}
return rl, nil
}
// Run runs the env list action.
func (pl *PkgList) Run() error {
registries, err := pl.rm.List(pl.app)
if err != nil {
return err
}
var rows [][]string
appLibraries, err := pl.app.Libraries()
if err != nil {
return err
}
for _, r := range registries {
spec, err := r.FetchRegistrySpec()
if err != nil {
return err
}
for libName := range spec.Libraries {
row := []string{r.Name(), libName}
_, isInstalled := appLibraries[libName]
if isInstalled {
row = append(row, pkgInstalled)
}
rows = append(rows, row)
}
}
sort.Slice(rows, func(i, j int) bool {
nameI := strings.Join([]string{rows[i][0], rows[i][1]}, "-")
nameJ := strings.Join([]string{rows[j][0], rows[j][1]}, "-")
return nameI < nameJ
})
t := table.New(pl.out)
t.SetHeader([]string{"registry", "name", "installed"})
t.AppendBulk(rows)
return t.Render()
}
// Copyright 2018 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 actions
import (
"bytes"
"testing"
"github.com/ksonnet/ksonnet/metadata/app"
amocks "github.com/ksonnet/ksonnet/metadata/app/mocks"
"github.com/ksonnet/ksonnet/pkg/registry"
rmocks "github.com/ksonnet/ksonnet/pkg/registry/mocks"
"github.com/stretchr/testify/require"
)
func TestPkgList(t *testing.T) {
withApp(t, func(appMock *amocks.App) {
libaries := app.LibraryRefSpecs{
"lib1": &app.LibraryRefSpec{},
}
appMock.On("Libraries").Return(libaries, nil)
a, err := NewPkgList(appMock)
require.NoError(t, err)
var buf bytes.Buffer
a.out = &buf
rm := &rmocks.Manager{}
a.rm = rm
spec := &registry.Spec{
Libraries: registry.LibraryRefSpecs{
"lib1": &registry.LibraryRef{},
"lib2": &registry.LibraryRef{},
},
}
incubator := mockRegistry("incubator")
incubator.On("FetchRegistrySpec").Return(spec, nil)
registries := []registry.Registry{incubator}
rm.On("List", appMock).Return(registries, nil)
err = a.Run()
require.NoError(t, err)
assertOutput(t, "pkg/list/output.txt", buf.String())
})
}
// Copyright 2018 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 actions
import (
"net/url"
"strings"
"github.com/ksonnet/ksonnet/metadata/app"
"github.com/ksonnet/ksonnet/pkg/registry"
)
// RunRegistryAdd runs `registry add`
func RunRegistryAdd(ksApp app.App, name, uri, version string) error {
nl, err := NewRegistryAdd(ksApp, name, uri, version)
if err != nil {
return err
}
return nl.Run()
}
// RegistryAdd lists namespaces.
type RegistryAdd struct {
app app.App
name string
uri string
version string
rm registry.Manager
}
// NewRegistryAdd creates an instance of RegistryAdd.
func NewRegistryAdd(ksApp app.App, name, uri, version string) (*RegistryAdd, error) {
ra := &RegistryAdd{
app: ksApp,
name: name,
uri: uri,
version: version,
rm: registry.DefaultManager,
}
return ra, nil
}
// Run lists namespaces.
func (ra *RegistryAdd) Run() error {
uri, protocol := ra.protocol()
_, err := ra.rm.Add(ra.app, ra.name, protocol, uri, ra.version)
return err
}
func (ra *RegistryAdd) protocol() (string, string) {
if strings.HasPrefix(ra.uri, "file://") {
return ra.uri, registry.ProtocolFilesystem
}
if strings.HasPrefix(ra.uri, "/") {
u := url.URL{
Scheme: "file",
Path: ra.uri,
}
return u.String(), registry.ProtocolFilesystem
}
return ra.uri, registry.ProtocolGitHub
}
// Copyright 2018 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 actions
import (
"testing"
amocks "github.com/ksonnet/ksonnet/metadata/app/mocks"
"github.com/ksonnet/ksonnet/pkg/registry"
"github.com/ksonnet/ksonnet/pkg/registry/mocks"
"github.com/stretchr/testify/require"
)
func TestRegistryAdd(t *testing.T) {
withApp(t, func(appMock *amocks.App) {
name := "new"
cases := []struct {
name string
uri string
version string
expectedURI string
protocol string
}{
{
name: "github",
uri: "github.com/foo/bar",
expectedURI: "github.com/foo/bar",
protocol: registry.ProtocolGitHub,
},
{
name: "fs",
uri: "/path",
expectedURI: "file:///path",
protocol: registry.ProtocolFilesystem,
},
{
name: "fs with URL",
uri: "file:///path",
expectedURI: "file:///path",
protocol: registry.ProtocolFilesystem,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
a, err := NewRegistryAdd(appMock, name, tc.uri, tc.version)
require.NoError(t, err)
rm := &mocks.Manager{}
rm.On("Add", appMock, "new", tc.protocol, tc.expectedURI, tc.version).Return(nil, nil)
a.rm = rm
err = a.Run()
require.NoError(t, err)
})
}
})
}
......@@ -54,7 +54,7 @@ func NewRegistryList(ksApp app.App) (*RegistryList, error) {
// Run runs the env list action.
func (rl *RegistryList) Run() error {
registries, err := rl.rm.Registries(rl.app)
registries, err := rl.rm.List(rl.app)
if err != nil {
return err
}
......
......@@ -39,7 +39,7 @@ func TestRegistryList(t *testing.T) {
registries := []registry.Registry{
mockRegistry("incubator"),
}
rm.On("Registries", appMock).Return(registries, nil)
rm.On("List", appMock).Return(registries, nil)
err = a.Run()
require.NoError(t, err)
......
REGISTRY NAME INSTALLED
======== ==== =========
incubator lib1 *
incubator lib2
......@@ -18,12 +18,10 @@ package cmd
import (
"fmt"
"os"
"sort"
"strings"
"github.com/ksonnet/ksonnet/metadata"
"github.com/ksonnet/ksonnet/metadata/parts"
"github.com/ksonnet/ksonnet/pkg/util/table"
"github.com/ksonnet/ksonnet/pkg/parts"
"github.com/spf13/cobra"
)
......@@ -41,10 +39,7 @@ var errInvalidSpec = fmt.Errorf("Command 'pkg install' requires a single argumen
func init() {
RootCmd.AddCommand(pkgCmd)
pkgCmd.AddCommand(pkgInstallCmd)
pkgCmd.AddCommand(pkgListCmd)
pkgCmd.AddCommand(pkgDescribeCmd)
pkgInstallCmd.PersistentFlags().String(flagName, "", "Name to give the dependency, to use within the ksonnet app")
}
var pkgCmd = &cobra.Command{
......@@ -84,67 +79,6 @@ See the annotated file tree below, as an example:
},
}
var pkgInstallCmd = &cobra.Command{
Use: "install <registry>/<library>@<version>",
Short: pkgShortDesc["install"],
Aliases: []string{"get"},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("Command requires a single argument of the form <registry>/<library>@<version>\n\n%s", cmd.UsageString())
}
registry, libID, name, version, err := parseDepSpec(cmd, args[0])
if err != nil {
return err
}
cwd, err := os.Getwd()
if err != nil {
return err
}
manager, err := metadata.Find(cwd)
if err != nil {
return err
}
_, err = manager.CacheDependency(registry, libID, name, version)
if err != nil {
return err
}
return nil
},
Long: `
The ` + "`install`" + ` command caches a ksonnet library locally, and makes it available
for use in the current ksonnet application. Enough info and metadata is recorded in
` + "`app.yaml` " + `that new users can retrieve the dependency after a fresh clone of this app.
The library itself needs to be located in a registry (e.g. Github repo). By default,
ksonnet knows about two registries: *incubator* and *stable*, which are the release
channels for official ksonnet libraries.
### Related Commands
* ` + "`ks pkg list` " + `— ` + pkgShortDesc["list"] + `
* ` + "`ks prototype list` " + `— ` + protoShortDesc["list"] + `
* ` + "`ks registry describe` " + `— ` + regShortDesc["describe"] + `
### Syntax
`,
Example: `
# Install an nginx dependency, based on the latest branch.
# In a ksonnet source file, this can be referenced as:
# local nginx = import "incubator/nginx/nginx.libsonnet";
ks pkg install incubator/nginx
# Install an nginx dependency, based on the 'master' branch.
# In a ksonnet source file, this can be referenced as:
# local nginx = import "incubator/nginx/nginx.libsonnet";
ks pkg install incubator/nginx@master
`,
}
var pkgDescribeCmd = &cobra.Command{
Use: "describe [<registry-name>/]<package-name>",
Short: pkgShortDesc["describe"],
......@@ -218,90 +152,6 @@ known ` + "`<registry-name>`" + ` like *incubator*). The output includes:
`,
}
var pkgListCmd = &cobra.Command{
Use: "list",
Short: pkgShortDesc["list"],
RunE: func(cmd *cobra.Command, args []string) error {
const (
nameHeader = "NAME"
registryHeader = "REGISTRY"
installedHeader = "INSTALLED"
installed = "*"
)
if len(args) != 0 {
return fmt.Errorf("Command 'pkg list' does not take arguments")
}
cwd, err := os.Getwd()
if err != nil {
return err
}
manager, err := metadata.Find(cwd)
if err != nil {
return err
}
app, err := manager.App()
if err != nil {
return err
}
t := table.New(os.Stdout)
t.SetHeader([]string{registryHeader, nameHeader, installedHeader})
rows := make([][]string, 0)
for name := range app.Registries() {
reg, _, err := manager.GetRegistry(name)
if err != nil {
return err
}
for libName := range reg.Libraries {
var row []string
_, isInstalled := app.Libraries()[libName]
if isInstalled {
row = []string{name, libName, installed}
} else {
row = []string{name, libName}
}
rows = append(rows, row)
}
}
sort.Slice(rows, func(i, j int) bool {
nameI := strings.Join([]string{rows[i][0], rows[i][1]}, "-")
nameJ := strings.Join([]string{rows[j][0], rows[j][1]}, "-")
return nameI < nameJ
})
t.AppendBulk(rows)
t.Render()
return nil
},
Long: `
The ` + "`list`" + ` command outputs a table that describes all *known* packages (not
necessarily downloaded, but available from existing registries). This includes
the following info:
1. Library name
2. Registry name
3. Installed status — an asterisk indicates 'installed'
### Related Commands
* ` + "`ks pkg install` " + `— ` + pkgShortDesc["install"] + `
* ` + "`ks pkg describe` " + `— ` + pkgShortDesc["describe"] + `
* ` + "`ks registry describe` " + `— ` + regShortDesc["describe"] + `
### Syntax
`,
}
func parsePkgSpec(spec string) (registry, libID string, err error) {
split := strings.SplitN(spec, "/", 2)
if len(split) < 2 {
......@@ -312,31 +162,3 @@ func parsePkgSpec(spec string) (registry, libID string, err error) {
libID = strings.SplitN(split[1], "@", 2)[0]
return
}
func parseDepSpec(cmd *cobra.Command, spec string) (registry, libID, name, version string, err error) {
registry, libID, err = parsePkgSpec(spec)
if err != nil {
return "", "", "", "", err
}
split := strings.Split(spec, "@")
if len(split) > 2 {
return "", "", "", "", fmt.Errorf("Symbol '@' is only allowed once, at the end of the argument of the form <registry>/<library>@<version>")
}
version = ""
if len(split) == 2 {
version = split[1]
}
name, err = cmd.Flags().GetString(flagName)
if err != nil {
return "", "", "", "", err
} else if name == "" {
// Get last component, strip off trailing `@<version>`.
split = strings.Split(spec, "/")
lastComponent := split[len(split)-1]
name = strings.SplitN(lastComponent, "@", 2)[0]
}
return
}
// Copyright 2018 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 cmd
import (
"fmt"
"github.com/spf13/viper"
"github.com/ksonnet/ksonnet/actions"
"github.com/spf13/cobra"
)
var (
vPkgInstallName = "pkg-install-name"
)
var pkgInstallCmd = &cobra.Command{
Use: "install <registry>/<library>@<version>",
Short: pkgShortDesc["install"],
Aliases: []string{"get"},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("Command requires a single argument of the form <registry>/<library>@<version>\n\n%s", cmd.UsageString())
}
customName := viper.GetString(vPkgInstallName)
return actions.RunPkgInstall(ka, args[0], customName)
},
Long: `
The ` + "`install`" + ` command caches a ksonnet library locally, and makes it available
for use in the current ksonnet application. Enough info and metadata is recorded in
` + "`app.yaml` " + `that new users can retrieve the dependency after a fresh clone of this app.
The library itself needs to be located in a registry (e.g. Github repo). By default,
ksonnet knows about two registries: *incubator* and *stable*, which are the release
channels for official ksonnet libraries.
### Related Commands
* ` + "`ks pkg list` " + `— ` + pkgShortDesc["list"] + `
* ` + "`ks prototype list` " + `— ` + protoShortDesc["list"] + `
* ` + "`ks registry describe` " + `— ` + regShortDesc["describe"] + `
### Syntax
`,
Example: `
# Install an nginx dependency, based on the latest branch.
# In a ksonnet source file, this can be referenced as:
# local nginx = import "incubator/nginx/nginx.libsonnet";
ks pkg install incubator/nginx
# Install an nginx dependency, based on the 'master' branch.
# In a ksonnet source file, this can be referenced as:
# local nginx = import "incubator/nginx/nginx.libsonnet";
ks pkg install incubator/nginx@master
`,
}
func init() {
pkgCmd.AddCommand(pkgInstallCmd)
pkgInstallCmd.Flags().String(flagName, "", "Name to give the dependency, to use within the ksonnet app")
viper.BindPFlag(vPkgInstallName, pkgInstallCmd.Flags().Lookup(flagName))
}
// Copyright 2018 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 cmd
import (
"fmt"
"github.com/ksonnet/ksonnet/actions"
"github.com/spf13/cobra"
)
var pkgListCmd = &cobra.Command{
Use: "list",
Short: pkgShortDesc["list"],
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 0 {
return fmt.Errorf("Command 'pkg list' does not take arguments")
}
return actions.RunPkgList(ka)
},
Long: `
The ` + "`list`" + ` command outputs a table that describes all *known* packages (not
necessarily downloaded, but available from existing registries). This includes
the following info:
1. Library name
2. Registry name
3. Installed status — an asterisk indicates 'installed'
### Related Commands
* ` + "`ks pkg install` " + `— ` + pkgShortDesc["install"] + `
* ` + "`ks pkg describe` " + `— ` + pkgShortDesc["describe"] + `
* ` + "`ks registry describe` " + `— ` + regShortDesc["describe"] + `
### Syntax
`,
}
func init() {
pkgCmd.AddCommand(pkgListCmd)
}
......@@ -22,7 +22,6 @@ import (
"strings"
"github.com/ksonnet/ksonnet/metadata"
"github.com/ksonnet/ksonnet/pkg/kubecfg"
"github.com/spf13/cobra"
)
......@@ -38,7 +37,6 @@ var regShortDesc = map[string]string{
func init() {
RootCmd.AddCommand(registryCmd)
registryCmd.AddCommand(registryListCmd)
registryCmd.AddCommand(registryDescribeCmd)
registryCmd.AddCommand(registryAddCmd)
......@@ -97,7 +95,11 @@ var registryDescribeCmd = &cobra.Command{
return err
}
regRef, exists := app.Registries()[name]
appRegistries, err := app.Registries()
if err != nil {
return err
}
regRef, exists := appRegistries[name]
if !exists {
return fmt.Errorf("Registry '%s' doesn't exist", name)
}
......@@ -145,59 +147,3 @@ 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,
provided that their file structures follow the appropriate schema. *You can look
at the ` + "`incubator`" + ` repo (https://github.com/ksonnet/parts/tree/master/incubator)
as an example.*
A registry is uniquely identified by its:
1. Name (e.g. ` + "`incubator`" + `)
2. Version (e.g. ` + "`master`" + `)
Currently, only registries supporting the **GitHub protocol** can be added.
During creation, all registries must specify a unique name and URI where the
registry lives. Optionally, a version can be provided (e.g. the *Github branch
name*). 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/tree/master/reg' and the version (branch name) 0.0.1
# NOTE that "0.0.1" overrides the branch name in the URI ("master")
ks registry add databases github.com/example/tree/master/reg --version=0.0.1`,
}
// Copyright 2018 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 cmd
import (
"fmt"
"github.com/ksonnet/ksonnet/actions"
"github.com/spf13/cobra"
)
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
}
return actions.RunRegistryAdd(ka, name, uri, version)
},
Long: `
The ` + "`add`" + ` command allows custom registries to be added to your ksonnet app,
provided that their file structures follow the appropriate schema. *You can look
at the ` + "`incubator`" + ` repo (https://github.com/ksonnet/parts/tree/master/incubator)
as an example.*
A registry is uniquely identified by its:
1. Name (e.g. ` + "`incubator`" + `)
2. Version (e.g. ` + "`master`" + `)
Currently, only registries supporting the **GitHub protocol** can be added.
During creation, all registries must specify a unique name and URI where the
registry lives. Optionally, a version can be provided (e.g. the *Github branch
name*). 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/tree/master/reg' and the version (branch name) 0.0.1
# NOTE that "0.0.1" overrides the branch name in the URI ("master")
ks registry add databases github.com/example/tree/master/reg --version=0.0.1`,
}
......@@ -47,3 +47,7 @@ table includes the following info:
### Syntax
`,
}
func init() {
registryCmd.AddCommand(registryListCmd)
}
......@@ -63,6 +63,13 @@ func (a *app) paramList(args ...string) *output {
return o
}
func (a *app) pkgList() *output {
o := a.runKs("pkg", "list")
assertExitStatus(o, 0)
return o
}
func (a *app) paramSet(key, value string, args ...string) *output {
o := a.runKs(append([]string{"param", "set", key, value}, args...)...)
assertExitStatus(o, 0)
......@@ -70,6 +77,20 @@ func (a *app) paramSet(key, value string, args ...string) *output {
return o
}
func (a *app) registryAdd(registryName, uri string) *output {
o := a.runKs("registry", "add", registryName, uri)
assertExitStatus(o, 0)
return o
}
func (a *app) registryList(args ...string) *output {
o := a.runKs(append([]string{"registry", "list"}, args...)...)
assertExitStatus(o, 0)
return o
}
func (a *app) generateDeployedService() {
appDir := a.dir
......
......@@ -16,7 +16,10 @@
package e2e
import (
"bytes"
"html/template"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"strings"
......@@ -77,3 +80,34 @@ func assertContents(name, path string) {
"expected output to be:\n%s\nit was:\n%s\n",
expected, got)
}
func assertTemplate(data interface{}, name, output string) {
path := filepath.Join("testdata", "output", name)
ExpectWithOffset(1, path).To(BeAnExistingFile())
t, err := template.ParseFiles(path)
ExpectWithOffset(1, err).ToNot(HaveOccurred())
var buf bytes.Buffer
err = t.Execute(&buf, data)
ExpectWithOffset(1, err).ToNot(HaveOccurred())
expected := buf.String()
got := output
ExpectWithOffset(1, expected).To(Equal(got),
"expected output to be:\n%s\nit was:\n%s\n",
expected, got)
}
func convertPathToURI(path string) string {
if strings.HasPrefix(path, "file://") {
return path
}
u := url.URL{
Scheme: "file",
Path: path,
}
return u.String()
}
......@@ -31,7 +31,7 @@ var _ = Describe("ks pkg", func() {
a = e.initApp("")
})
Describe("describe", func() {
Describe("add", func() {
Context("incubator/apache", func() {
It("describes the package", func() {
o := a.runKs("pkg", "describe", "incubator/apache")
......@@ -41,16 +41,49 @@ var _ = Describe("ks pkg", func() {
})
})
Describe("install", func() {
Describe("describe", func() {
Context("incubator/apache", func() {
It("describes the package", func() {
o := a.runKs("pkg", "install", "incubator/apache")
o := a.runKs("pkg", "describe", "incubator/apache")
assertExitStatus(o, 0)
assertOutput("pkg/describe/output.txt", o.stdout)
})
})
})
Describe("install", func() {
Context("github based part", func() {
Context("incubator/apache", func() {
It("describes the package", func() {
o := a.runKs("pkg", "install", "incubator/apache")
assertExitStatus(o, 0)
pkgDir := filepath.Join(a.dir, "vendor", "incubator", "apache")
Expect(pkgDir).To(BeADirectory())
pkgDir := filepath.Join(a.dir, "vendor", "incubator", "apache")
Expect(pkgDir).To(BeADirectory())
})
})
})
Context("fs based part", func() {
Context("local/contour", func() {
It("describes the package", func() {
path, err := filepath.Abs(filepath.Join("testdata", "registries", "parts-infra"))
Expect(err).ToNot(HaveOccurred())
o := a.registryAdd("local", path)
o = a.runKs("pkg", "install", "local/contour")
assertExitStatus(o, 0)
o = a.pkgList()
m := map[string]interface{}{}
tPath := filepath.Join("pkg", "install", "fs-output.txt.tmpl")
assertTemplate(m, tPath, o.stdout)
})
})
})
})
Describe("list", func() {
......
......@@ -18,7 +18,10 @@
package e2e
import (
"path/filepath"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("ks registry", func() {
......@@ -29,6 +32,46 @@ var _ = Describe("ks registry", func() {
a.generateDeployedService()
})
Describe("add", func() {
var add = func(path string) {
o := a.runKs("registry", "add", "local", path)
assertExitStatus(o, 0)
uri := convertPathToURI(path)
m := map[string]interface{}{
"uri": uri,
}
o = a.registryList()
tPath := filepath.Join("registry", "add", "output.txt.tmpl")
assertTemplate(m, tPath, o.stdout)
}
Context("a filesystem based registry", func() {
Context("as a path", func() {
It("adds a registry", func() {
path, err := filepath.Abs(filepath.Join("testdata", "registries", "parts-infra"))
Expect(err).ToNot(HaveOccurred())
add(path)
})
})
Context("as a URL", func() {
It("adds a registry", func() {
path, err := filepath.Abs(filepath.Join("testdata", "registries", "parts-infra"))
Expect(err).ToNot(HaveOccurred())
uri := convertPathToURI(path)
add(uri)
})
})
})
})
Describe("list", func() {
It("lists the currently configured registries", func() {
o := a.runKs("registry", "list")
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment