Unverified Commit 9a0985f3 authored by bryanl's avatar bryanl
Browse files

create package install checker

Signed-off-by: bryanl bryanliles@gmail.com
parent 6221d1ec
/kubecfg /kubecfg
/ks /ks
/ks.exe
/cmd/ks/debug /cmd/ks/debug
# Compiled Object files, Static and Dynamic libs (Shared Objects) debug.test
*.o
*.a
*.so
# Folders /_output
_obj /dist
_test
_output
dist
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
# gotags
tags
.DS_Store
...@@ -16,12 +16,11 @@ ...@@ -16,12 +16,11 @@
package actions package actions
import ( import (
"fmt"
"io" "io"
"os" "os"
"text/template"
"github.com/ksonnet/ksonnet/pkg/app" "github.com/ksonnet/ksonnet/pkg/app"
"github.com/ksonnet/ksonnet/pkg/pkg"
"github.com/ksonnet/ksonnet/pkg/registry" "github.com/ksonnet/ksonnet/pkg/registry"
) )
...@@ -37,24 +36,26 @@ func RunPkgDescribe(m map[string]interface{}) error { ...@@ -37,24 +36,26 @@ func RunPkgDescribe(m map[string]interface{}) error {
// PkgDescribe describes a package. // PkgDescribe describes a package.
type PkgDescribe struct { type PkgDescribe struct {
app app.App app app.App
pkgName string pkgName string
templateSrc string
out io.Writer out io.Writer
libPartFn func(app.App, string) (*pkg.Package, error) packageManager registry.PackageManager
registryPartFn func(app.App, string) (*pkg.Package, error)
} }
// NewPkgDescribe creates an instance of PkgDescribe. // NewPkgDescribe creates an instance of PkgDescribe.
func NewPkgDescribe(m map[string]interface{}) (*PkgDescribe, error) { func NewPkgDescribe(m map[string]interface{}) (*PkgDescribe, error) {
ol := newOptionLoader(m) ol := newOptionLoader(m)
app := ol.LoadApp()
pd := &PkgDescribe{ pd := &PkgDescribe{
app: ol.LoadApp(), app: app,
pkgName: ol.LoadString(OptionPackageName), pkgName: ol.LoadString(OptionPackageName),
templateSrc: pkgDescribeTemplate,
out: os.Stdout, out: os.Stdout,
libPartFn: pkg.Find, packageManager: registry.NewPackageManager(app),
registryPartFn: registry.Package,
} }
if ol.err != nil { if ol.err != nil {
...@@ -66,35 +67,53 @@ func NewPkgDescribe(m map[string]interface{}) (*PkgDescribe, error) { ...@@ -66,35 +67,53 @@ func NewPkgDescribe(m map[string]interface{}) (*PkgDescribe, error) {
// Run describes a package. // Run describes a package.
func (pd *PkgDescribe) Run() error { func (pd *PkgDescribe) Run() error {
d, err := pkg.ParseName(pd.pkgName) p, err := pd.packageManager.Find(pd.pkgName)
if err != nil { if err != nil {
return err return err
} }
var p *pkg.Package data := map[string]interface{}{
if d.Registry == "" { "Name": pd.pkgName,
p, err = pd.libPartFn(pd.app, pd.pkgName) "Description": p.Description(),
if err != nil { }
return err
} isInstalled, err := p.IsInstalled()
} else { if err != nil {
p, err = pd.registryPartFn(pd.app, pd.pkgName) return err
}
data["IsInstalled"] = isInstalled
if isInstalled {
prototypes, err := p.Prototypes()
if err != nil { if err != nil {
return err return err
} }
data["Prototypes"] = prototypes
} }
fmt.Fprintln(pd.out, `LIBRARY NAME:`) t, err := template.New("pkg-describe").Parse(pd.templateSrc)
fmt.Fprintln(pd.out, p.Name) if err != nil {
fmt.Fprintln(pd.out) return err
fmt.Fprintln(pd.out, `DESCRIPTION:`) }
fmt.Fprintln(pd.out, p.Description)
fmt.Fprintln(pd.out)
fmt.Fprintln(pd.out, `PROTOTYPES:`)
for _, proto := range p.Prototypes { if err = t.Execute(pd.out, data); err != nil {
fmt.Fprintf(pd.out, " %s\n", proto.Name) return err
} }
return nil return nil
} }
const pkgDescribeTemplate = `LIBRARY NAME:
{{.Name}}
DESCRIPTION:
{{.Description}}
{{- if .IsInstalled}}
PROTOTYPES:{{- range .Prototypes}}
{{.Name}} - {{.Template.ShortDescription}}
{{- end}}{{- end}}
`
...@@ -17,80 +17,174 @@ package actions ...@@ -17,80 +17,174 @@ package actions
import ( import (
"bytes" "bytes"
"io"
"testing" "testing"
"github.com/ksonnet/ksonnet/pkg/app" "github.com/ksonnet/ksonnet/pkg/app"
"github.com/ksonnet/ksonnet/pkg/prototype"
"github.com/pkg/errors"
amocks "github.com/ksonnet/ksonnet/pkg/app/mocks" amocks "github.com/ksonnet/ksonnet/pkg/app/mocks"
"github.com/ksonnet/ksonnet/pkg/pkg" pkgmocks "github.com/ksonnet/ksonnet/pkg/pkg/mocks"
"github.com/ksonnet/ksonnet/pkg/registry"
regmocks "github.com/ksonnet/ksonnet/pkg/registry/mocks"
"github.com/ksonnet/ksonnet/pkg/util/test"
"github.com/spf13/afero"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestPkgDescribe_library(t *testing.T) { func TestPkgDescribe(t *testing.T) {
withApp(t, func(appMock *amocks.App) {
libaries := app.LibraryConfigs{ cases := []struct {
"apache": &app.LibraryConfig{}, name string
} output string
appMock.On("Libraries").Return(libaries, nil) pkgManager func() registry.PackageManager
isErr bool
in := map[string]interface{}{ templateSrc string
OptionApp: appMock, out io.Writer
OptionPackageName: "apache", }{
} {
name: "with no prototypes",
a, err := NewPkgDescribe(in) output: "pkg/describe/output.txt",
require.NoError(t, err) pkgManager: func() registry.PackageManager {
p := &pkgmocks.Package{}
var buf bytes.Buffer p.On("Description").Return("description")
a.out = &buf p.On("IsInstalled").Return(false, nil)
a.libPartFn = func(a app.App, name string) (*pkg.Package, error) { pkgManager := &regmocks.PackageManager{}
p := &pkg.Package{ pkgManager.On("Find", "apache").Return(p, nil)
Name: "apache",
Description: "description", return pkgManager
} },
},
return p, nil {
} name: "with prototypes",
output: "pkg/describe/with-prototypes.txt",
err = a.Run() pkgManager: func() registry.PackageManager {
require.NoError(t, err) prototypes := prototype.Prototypes{
{
assertOutput(t, "pkg/describe/output.txt", buf.String()) Name: "proto1",
}) Template: prototype.SnippetSchema{
} ShortDescription: "short description",
},
},
}
p := &pkgmocks.Package{}
p.On("Description").Return("description")
p.On("IsInstalled").Return(true, nil)
p.On("Prototypes").Return(prototypes, nil)
pkgManager := &regmocks.PackageManager{}
pkgManager.On("Find", "apache").Return(p, nil)
return pkgManager
},
},
{
name: "package manager find error",
isErr: true,
pkgManager: func() registry.PackageManager {
pkgManager := &regmocks.PackageManager{}
pkgManager.On("Find", "apache").Return(nil, errors.New("failed"))
return pkgManager
},
},
{
name: "check installed returns error",
isErr: true,
pkgManager: func() registry.PackageManager {
p := &pkgmocks.Package{}
p.On("Description").Return("description")
p.On("IsInstalled").Return(false, errors.New("failed"))
pkgManager := &regmocks.PackageManager{}
pkgManager.On("Find", "apache").Return(p, nil)
return pkgManager
},
},
{
name: "gather prototypes returns error",
isErr: true,
pkgManager: func() registry.PackageManager {
p := &pkgmocks.Package{}
p.On("Prototypes").Return(nil, errors.New("failed"))
p.On("Description").Return("description")
p.On("IsInstalled").Return(true, nil)
pkgManager := &regmocks.PackageManager{}
pkgManager.On("Find", "apache").Return(p, nil)
return pkgManager
},
},
{
name: "template is invalid",
isErr: true,
pkgManager: func() registry.PackageManager {
p := &pkgmocks.Package{}
p.On("Description").Return("description")
p.On("IsInstalled").Return(false, nil)
pkgManager := &regmocks.PackageManager{}
pkgManager.On("Find", "apache").Return(p, nil)
return pkgManager
},
templateSrc: "{{-abc}}",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Fatal(r)
}
}()
test.WithApp(t, "/app", func(a *amocks.App, fs afero.Fs) {
libraries := app.LibraryRefSpecs{
"apache": &app.LibraryRefSpec{},
}
func TestPkgDescribe_registry(t *testing.T) { func TestPkgDescribe_registry(t *testing.T) {
withApp(t, func(appMock *amocks.App) { withApp(t, func(appMock *amocks.App) {
registries := app.RegistryConfigs{ registries := app.RegistryConfigs{
"incubator": &app.RegistryConfig{}, "incubator": &app.RegistryConfig{},
} }
appMock.On("Registries").Return(registries, nil) a.On("Libraries").Return(libraries, nil)
in := map[string]interface{}{ in := map[string]interface{}{
OptionApp: appMock, OptionApp: a,
OptionPackageName: "incubator/apache", OptionPackageName: "apache",
} }
a, err := NewPkgDescribe(in) pd, err := NewPkgDescribe(in)
require.NoError(t, err) require.NoError(t, err)
var buf bytes.Buffer if tc.templateSrc != "" {
a.out = &buf pd.templateSrc = tc.templateSrc
}
a.registryPartFn = func(a app.App, name string) (*pkg.Package, error) { pd.packageManager = tc.pkgManager()
p := &pkg.Package{
Name: "apache",
Description: "description",
}
return p, nil var buf bytes.Buffer
} pd.out = &buf
err = a.Run() err = pd.Run()
require.NoError(t, err) if tc.isErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assertOutput(t, "pkg/describe/output.txt", buf.String()) assertOutput(t, tc.output, buf.String())
}) })
})
}
} }
func TestPkgDescribe_requires_app(t *testing.T) { func TestPkgDescribe_requires_app(t *testing.T) {
......
...@@ -24,6 +24,7 @@ import ( ...@@ -24,6 +24,7 @@ import (
"github.com/ksonnet/ksonnet/pkg/app" "github.com/ksonnet/ksonnet/pkg/app"
"github.com/ksonnet/ksonnet/pkg/pkg" "github.com/ksonnet/ksonnet/pkg/pkg"
"github.com/ksonnet/ksonnet/pkg/prototype" "github.com/ksonnet/ksonnet/pkg/prototype"
"github.com/ksonnet/ksonnet/pkg/registry"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
...@@ -39,22 +40,23 @@ func RunPrototypeDescribe(m map[string]interface{}) error { ...@@ -39,22 +40,23 @@ func RunPrototypeDescribe(m map[string]interface{}) error {
// PrototypeDescribe describes a prototype. // PrototypeDescribe describes a prototype.
type PrototypeDescribe struct { type PrototypeDescribe struct {
app app.App app app.App
out io.Writer out io.Writer
query string query string
appPrototypesFn func(app.App, pkg.Descriptor) (prototype.Prototypes, error) packageManager registry.PackageManager
} }
// NewPrototypeDescribe creates an instance of PrototypeDescribe // NewPrototypeDescribe creates an instance of PrototypeDescribe
func NewPrototypeDescribe(m map[string]interface{}) (*PrototypeDescribe, error) { func NewPrototypeDescribe(m map[string]interface{}) (*PrototypeDescribe, error) {
ol := newOptionLoader(m) ol := newOptionLoader(m)
app := ol.LoadApp()
pd := &PrototypeDescribe{ pd := &PrototypeDescribe{
app: ol.LoadApp(), app: app,
query: ol.LoadString(OptionQuery), query: ol.LoadString(OptionQuery),
out: os.Stdout, out: os.Stdout,
appPrototypesFn: pkg.LoadPrototypes, packageManager: registry.NewPackageManager(app),
} }
if ol.err != nil { if ol.err != nil {
...@@ -66,7 +68,7 @@ func NewPrototypeDescribe(m map[string]interface{}) (*PrototypeDescribe, error) ...@@ -66,7 +68,7 @@ func NewPrototypeDescribe(m map[string]interface{}) (*PrototypeDescribe, error)
// Run runs the env list action. // Run runs the env list action.
func (pd *PrototypeDescribe) Run() error { func (pd *PrototypeDescribe) Run() error {
prototypes, err := allPrototypes(pd.app, pd.appPrototypesFn) prototypes, err := pd.packageManager.Prototypes()
if err != nil { if err != nil {
return err return err
} }
...@@ -106,35 +108,6 @@ func (pd *PrototypeDescribe) Run() error { ...@@ -106,35 +108,6 @@ func (pd *PrototypeDescribe) Run() error {
type prototypeFn func(app.App, pkg.Descriptor) (prototype.Prototypes, error) type prototypeFn func(app.App, pkg.Descriptor) (prototype.Prototypes, error)
func allPrototypes(a app.App, appPrototypes prototypeFn) (prototype.Prototypes, error) {
if a == nil {
return nil, errors.New("app is required")
}
libraries, err := a.Libraries()
if err != nil {
return nil, err
}
var prototypes prototype.Prototypes
for _, library := range libraries {
d := pkg.Descriptor{
Registry: library.Registry,
Part: library.Name,
}
p, err := appPrototypes(a, d)
if err != nil {
return nil, err
}
prototypes = append(prototypes, p...)
}
return prototypes, nil
}
func findUniquePrototype(query string, prototypes prototype.Prototypes) (*prototype.Prototype, error) { func findUniquePrototype(query string, prototypes prototype.Prototypes) (*prototype.Prototype, error) {
index, err := prototype.NewIndex(prototypes, prototype.DefaultBuilder) index, err := prototype.NewIndex(prototypes, prototype.DefaultBuilder)
if err != nil { if err != nil {
......
...@@ -19,16 +19,18 @@ import ( ...@@ -19,16 +19,18 @@ import (
"bytes" "bytes"
"testing" "testing"
"github.com/ksonnet/ksonnet/pkg/app"
amocks "github.com/ksonnet/ksonnet/pkg/app/mocks" amocks "github.com/ksonnet/ksonnet/pkg/app/mocks"
"github.com/ksonnet/ksonnet/pkg/prototype"
registrymocks "github.com/ksonnet/ksonnet/pkg/registry/mocks"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestPrototypeDescribe(t *testing.T) { func TestPrototypeDescribe(t *testing.T) {
withApp(t, func(appMock *amocks.App) { withApp(t, func(appMock *amocks.App) {
libaries := app.LibraryConfigs{} prototypes := prototype.Prototypes{}
appMock.On("Libraries").Return(libaries, nil) manager := &registrymocks.PackageManager{}
manager.On("Prototypes").Return(prototypes, nil)
in := map[string]interface{}{ in := map[string]interface{}{
OptionApp: appMock, OptionApp: appMock,
...@@ -38,6 +40,8 @@ func TestPrototypeDescribe(t *testing.T) { ...@@ -38,6 +40,8 @@ func TestPrototypeDescribe(t *testing.T) {
a, err := NewPrototypeDescribe(in) a, err := NewPrototypeDescribe(in)
require.NoError(t, err) require.NoError(t, err)
a.packageManager = manager
var buf bytes.Buffer var buf bytes.Buffer
a.out = &buf a.out = &buf
......
...@@ -21,8 +21,8 @@ import ( ...@@ -21,8 +21,8 @@ import (
"sort" "sort"
"github.com/ksonnet/ksonnet/pkg/app" "github.com/ksonnet/ksonnet/pkg/app"
"github.com/ksonnet/ksonnet/pkg/pkg"
"github.com/ksonnet/ksonnet/pkg/prototype" "github.com/ksonnet/ksonnet/pkg/prototype"
"github.com/ksonnet/ksonnet/pkg/registry"
"github.com/ksonnet/ksonnet/pkg/util/table" "github.com/ksonnet/ksonnet/pkg/util/table"
) )
...@@ -38,20 +38,20 @@ func RunPrototypeList(m map[string]interface{}) error { ...@@ -38,20 +38,20 @@ func RunPrototypeList(m map[string]interface{}) error {
// PrototypeList lists available prototypes. // PrototypeList lists available prototypes.
type PrototypeList struct { type PrototypeList struct {
app app.App