Unverified Commit 6f47d32a authored by Derek Wilson's avatar Derek Wilson Committed by GitHub
Browse files

Merge pull request #847 from underrun/issue-756-app-loading

fix -h, verison, and add global --dir flag with refactor
parents 004404df 3ff6fb1d
......@@ -31,6 +31,8 @@ import (
const (
// OptionApp is app option.
OptionApp = "app"
// OptionAppRoot is the root directory of the application.
OptionAppRoot = "app-root"
// OptionArguments is arguments option. Used for passing arguments to prototypes.
OptionArguments = "arguments"
// OptionAsString is asString. Used for setting values as strings.
......@@ -67,6 +69,8 @@ const (
OptionGlobal = "global"
// OptionGracePeriod is gracePeriod option.
OptionGracePeriod = "grace-period"
// OptionHTTPClient is the http.Client for outbound network requests.
OptionHTTPClient = "http-client"
// OptionInstalled is for listing installed packages.
OptionInstalled = "only-installed"
// OptionJPaths is jsonnet paths.
......@@ -79,6 +83,8 @@ const (
OptionModule = "module"
// OptionNamespace is a cluster namespace option
OptionNamespace = "namespace"
// OptionNewRoot is init new root path option.
OptionNewRoot = "root-path"
// OptionNewEnvName is newEnvName option. Used for renaming environments.
OptionNewEnvName = "new-env-name"
// OptionOutput is output option.
......@@ -94,12 +100,12 @@ const (
// OptionResolveImage is resolve image option. It is used to resolve docker image references
// when setting parameters.
OptionResolveImage = "resolve-image"
// OptionRootPath is path option.
OptionRootPath = "root-path"
// OptionServer is server option.
OptionServer = "server"
// OptionServerURI is serverURI option.
OptionServerURI = "server-uri"
// OptionSkipCheckUpgrade tells app not to emit upgrade warnings, probably because the user is already upgrading.
OptionSkipCheckUpgrade = "skip-check-upgrade"
// OptionSkipDefaultRegistries is skipDefaultRegistries option. Used by init.
OptionSkipDefaultRegistries = "skip-default-registries"
// OptionSkipGc is skipGc option.
......@@ -185,15 +191,15 @@ func newOptionLoader(m map[string]interface{}) *optionLoader {
}
}
func (o *optionLoader) LoadFs(name string) afero.Fs {
i := o.load(name)
func (o *optionLoader) LoadFs() afero.Fs {
i := o.loadOptional(OptionFs)
if i == nil {
return nil
return afero.NewOsFs()
}
a, ok := i.(afero.Fs)
if !ok {
o.err = newInvalidOptionError(name)
o.err = newInvalidOptionError(OptionFs)
return nil
}
......@@ -332,24 +338,63 @@ func (o *optionLoader) LoadClientConfig() *client.Config {
return a
}
// LoadApp returns an app.App reference - either as passed via OptionApp,
// or newly constructed.
func (o *optionLoader) LoadApp() app.App {
i := o.load(OptionApp)
if i == nil {
o.err = ErrNotInApp
i := o.loadOptional(OptionApp)
a, ok := i.(app.App)
if i != nil && !ok {
// App was provided but was invalid type
o.err = newInvalidOptionError(OptionApp)
return nil
}
if a != nil {
// Return app if a valid app.App was provided
return a
}
a, ok := i.(app.App)
if !ok {
o.err = newInvalidOptionError(OptionApp)
var fs = o.LoadFs()
if fs == nil {
o.err = errors.New("missing required fs reference")
return nil
}
var httpClient = o.LoadHTTPClient()
if httpClient == nil {
o.err = errors.New("initializing http client")
return nil
}
var appRoot = o.LoadOptionalString(OptionAppRoot)
appRoot, err := app.FindRoot(fs, appRoot)
if err != nil {
o.err = errors.Wrapf(err, "finding app root from starting path: %s", appRoot)
return nil
}
a, err = app.Load(fs, httpClient, appRoot)
if err != nil {
o.err = errors.New("initializing app")
return nil
}
if !o.LoadOptionalBool(OptionSkipCheckUpgrade) {
if _, err := a.CheckUpgrade(); err != nil {
o.err = errors.Wrap(err, "checking for app upgrades")
return nil
}
}
return a
}
// LoadHTTPClient loads an HTTP client based on common configuration for certificates, tls verification, timeouts, etc.
func (o *optionLoader) LoadHTTPClient() *http.Client {
i := o.loadOptional(OptionHTTPClient)
if c, ok := i.(*http.Client); ok {
return c
}
// Construct a client if none was passed
tlsSkipVerify := o.LoadOptionalBool(OptionTLSSkipVerify)
tlsConfig := &tls.Config{
......
......@@ -55,7 +55,6 @@ func Test_optionLoader_types(t *testing.T) {
},
{
name: "Fs",
hasArg: true,
valid: afero.NewMemMapFs(),
invalid: "invalid",
keyName: OptionFs,
......
......@@ -40,7 +40,7 @@ func RunInit(m map[string]interface{}) error {
return i.Run()
}
type appLoadFn func(fs afero.Fs, httpClient *http.Client, root string, skipFindRoot bool) (app.App, error)
type appLoadFn func(fs afero.Fs, httpClient *http.Client, root string) (app.App, error)
type appInitFn func(fs afero.Fs, httpClient *http.Client, name, rootPath, envName, k8sSpecFlag, serverURI, namespace string, registries []registry.Registry) error
......@@ -69,9 +69,9 @@ func NewInit(m map[string]interface{}) (*Init, error) {
ol := newOptionLoader(m)
i := &Init{
fs: ol.LoadFs(OptionFs),
fs: ol.LoadFs(),
name: ol.LoadString(OptionName),
rootPath: ol.LoadString(OptionRootPath),
rootPath: ol.LoadString(OptionNewRoot),
envName: ol.LoadString(OptionEnvName),
k8sSpecFlag: ol.LoadString(OptionSpecFlag),
serverURI: ol.LoadOptionalString(OptionServer),
......@@ -97,7 +97,7 @@ func (i *Init) Run() error {
var registries []registry.Registry
if !i.skipDefaultRegistries {
a, err := i.appLoadFn(i.fs, i.httpClient, i.rootPath, true)
a, err := i.appLoadFn(i.fs, i.httpClient, i.rootPath)
if err != nil {
return err
}
......
......@@ -60,7 +60,7 @@ func TestInit(t *testing.T) {
in := map[string]interface{}{
OptionFs: aFs,
OptionName: aName,
OptionRootPath: aRootPath,
OptionNewRoot: aRootPath,
OptionEnvName: tc.envName,
OptionSpecFlag: aK8sSpecFlag,
OptionServer: aServerURI,
......@@ -95,7 +95,7 @@ func TestInit(t *testing.T) {
return nil
}
a.appLoadFn = func(fs afero.Fs, httpClient *http.Client, root string, skipFindRoot bool) (app.App, error) {
a.appLoadFn = func(fs afero.Fs, httpClient *http.Client, root string) (app.App, error) {
return appMock, nil
}
......
......@@ -103,16 +103,8 @@ type App interface {
}
// Load loads the application configuration.
func Load(fs afero.Fs, httpClient *http.Client, cwd string, skipFindRoot bool) (App, error) {
func Load(fs afero.Fs, httpClient *http.Client, appRoot string) (App, error) {
log := log.WithField("action", "app.Load")
appRoot := cwd
if !skipFindRoot {
var err error
appRoot, err = findRoot(fs, cwd)
if err != nil {
return nil, err
}
}
spec, err := read(fs, appRoot)
if os.IsNotExist(err) {
......@@ -232,7 +224,7 @@ func cleanEnv(fs afero.Fs, root string) error {
return nil
}
func findRoot(fs afero.Fs, cwd string) (string, error) {
func FindRoot(fs afero.Fs, cwd string) (string, error) {
prev := cwd
for {
......
......@@ -22,7 +22,7 @@ import (
"github.com/stretchr/testify/require"
)
func Test_findRoot(t *testing.T) {
func Test_FindRoot(t *testing.T) {
fs := afero.NewMemMapFs()
stageFile(t, fs, "app010_app.yaml", "/app/app.yaml")
......@@ -61,7 +61,7 @@ func Test_findRoot(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
root, err := findRoot(fs, tc.name)
root, err := FindRoot(fs, tc.name)
if tc.isErr {
require.Error(t, err)
return
......
......@@ -75,7 +75,7 @@ func (i *initApp) Run() error {
}
// Load application.
a, err := app.Load(i.fs, i.httpClient, i.rootPath, false)
a, err := app.Load(i.fs, i.httpClient, i.rootPath)
if err != nil {
return err
}
......
......@@ -18,6 +18,7 @@ package clicmd
import (
"github.com/ksonnet/ksonnet/pkg/actions"
"github.com/pkg/errors"
"github.com/spf13/viper"
)
type initName int
......@@ -116,3 +117,8 @@ func runAction(name initName, args map[string]interface{}) error {
return fn(args)
}
func addGlobalOptions(m map[string]interface{}) {
m[actions.OptionTLSSkipVerify] = viper.GetBool(flagTLSSkipVerify)
m[actions.OptionAppRoot] = viper.GetString(flagDir)
}
......@@ -17,9 +17,9 @@ package clicmd
import (
"github.com/ksonnet/ksonnet/pkg/actions"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/ksonnet/ksonnet/pkg/client"
"github.com/pkg/errors"
"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
......@@ -82,8 +82,8 @@ ks apply dev -c guestbook-ui -c nginx-depl --create false
`
)
func newApplyCmd(a app.App) *cobra.Command {
applyClientConfig := client.NewDefaultClientConfig(a)
func newApplyCmd(fs afero.Fs) *cobra.Command {
applyClientConfig := client.NewDefaultClientConfig()
applyCmd := &cobra.Command{
Use: "apply <env-name> [-c <component-name>] [--dry-run]",
......@@ -95,7 +95,6 @@ func newApplyCmd(a app.App) *cobra.Command {
}
m := map[string]interface{}{
actions.OptionApp: a,
actions.OptionClientConfig: applyClientConfig,
actions.OptionComponentNames: viper.GetStringSlice(vApplyComponent),
actions.OptionCreate: viper.GetBool(vApplyCreate),
......@@ -104,8 +103,9 @@ func newApplyCmd(a app.App) *cobra.Command {
actions.OptionGcTag: viper.GetString(vApplyGcTag),
actions.OptionSkipGc: viper.GetBool(vApplySkipGc),
}
addGlobalOptions(m)
if err := extractJsonnetFlags(a, "apply"); err != nil {
if err := extractJsonnetFlags(fs, "apply"); err != nil {
return errors.Wrap(err, "handle jsonnet flags")
}
......
......@@ -19,11 +19,10 @@ import (
"fmt"
"strings"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/spf13/cobra"
)
func newComponentCmd(a app.App) *cobra.Command {
func newComponentCmd() *cobra.Command {
componentCmd := &cobra.Command{
Use: "component",
Short: "Manage ksonnet components",
......@@ -35,8 +34,8 @@ func newComponentCmd(a app.App) *cobra.Command {
},
}
componentCmd.AddCommand(newComponentListCmd(a))
componentCmd.AddCommand(newComponentRmCmd(a))
componentCmd.AddCommand(newComponentListCmd())
componentCmd.AddCommand(newComponentRmCmd())
return componentCmd
......
......@@ -19,7 +19,6 @@ import (
"fmt"
"github.com/ksonnet/ksonnet/pkg/actions"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
......@@ -38,7 +37,7 @@ The ` + "`list`" + ` command displays all known components.
ks component list`
)
func newComponentListCmd(a app.App) *cobra.Command {
func newComponentListCmd() *cobra.Command {
componentListCmd := &cobra.Command{
Use: "list",
Short: "List known components",
......@@ -50,10 +49,10 @@ func newComponentListCmd(a app.App) *cobra.Command {
}
m := map[string]interface{}{
actions.OptionApp: a,
actions.OptionModule: viper.GetString(vComponentListNamespace),
actions.OptionOutput: viper.GetString(vComponentListOutput),
}
addGlobalOptions(m)
return runAction(actionComponentList, m)
},
......
......@@ -19,7 +19,6 @@ import (
"fmt"
"github.com/ksonnet/ksonnet/pkg/actions"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/spf13/cobra"
)
......@@ -33,7 +32,7 @@ references throughout the project.`
ks component rm guestbook`
)
func newComponentRmCmd(a app.App) *cobra.Command {
func newComponentRmCmd() *cobra.Command {
componentRmCmd := &cobra.Command{
Use: "rm <component-name>",
Short: "Delete a component from the ksonnet application",
......@@ -45,9 +44,9 @@ func newComponentRmCmd(a app.App) *cobra.Command {
}
m := map[string]interface{}{
actions.OptionApp: a,
actions.OptionComponentName: args[0],
}
addGlobalOptions(m)
return runAction(actionComponentRm, m)
},
......
......@@ -17,9 +17,9 @@ package clicmd
import (
"github.com/ksonnet/ksonnet/pkg/actions"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/ksonnet/ksonnet/pkg/client"
"github.com/pkg/errors"
"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
......@@ -57,8 +57,8 @@ ks delete dev
ks delete --kubeconfig=./kubeconfig -c nginx`
)
func newDeleteCmd(a app.App) *cobra.Command {
deleteClientConfig := client.NewDefaultClientConfig(a)
func newDeleteCmd(fs afero.Fs) *cobra.Command {
deleteClientConfig := client.NewDefaultClientConfig()
deleteCmd := &cobra.Command{
Use: "delete [env-name] [-c <component-name>]",
......@@ -72,14 +72,14 @@ func newDeleteCmd(a app.App) *cobra.Command {
}
m := map[string]interface{}{
actions.OptionApp: a,
actions.OptionClientConfig: deleteClientConfig,
actions.OptionComponentNames: viper.GetStringSlice(vDeleteComponent),
actions.OptionEnvName: envName,
actions.OptionGracePeriod: viper.GetInt64(vDeleteGracePeriod),
}
addGlobalOptions(m)
if err := extractJsonnetFlags(a, "delete"); err != nil {
if err := extractJsonnetFlags(fs, "delete"); err != nil {
return errors.Wrap(err, "handle jsonnet flags")
}
......
......@@ -19,11 +19,11 @@ import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/ksonnet/ksonnet/pkg/actions"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/ksonnet/ksonnet/pkg/client"
)
......@@ -85,8 +85,8 @@ ks diff dev -c redis
`
)
func newDiffCmd(a app.App) *cobra.Command {
diffClientConfig := client.NewDefaultClientConfig(a)
func newDiffCmd(fs afero.Fs) *cobra.Command {
diffClientConfig := client.NewDefaultClientConfig()
diffCmd := &cobra.Command{
Use: "diff <location1:env1> [location2:env2]",
......@@ -102,17 +102,17 @@ func newDiffCmd(a app.App) *cobra.Command {
}
m := map[string]interface{}{
actions.OptionApp: a,
actions.OptionClientConfig: diffClientConfig,
actions.OptionSrc1: args[0],
actions.OptionComponentNames: viper.GetStringSlice(vDiffComponentNames),
}
addGlobalOptions(m)
if len(args) == 2 {
m[actions.OptionSrc2] = args[1]
}
if err := extractJsonnetFlags(a, "diff"); err != nil {
if err := extractJsonnetFlags(fs, "diff"); err != nil {
return errors.Wrap(err, "handle jsonnet flags")
}
......
......@@ -19,7 +19,6 @@ import (
"fmt"
"strings"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/ksonnet/ksonnet/pkg/client"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
......@@ -72,7 +71,7 @@ represented as a hierarchy in the ` + "`environments/`" + ` directory of a ksonn
`
)
func newEnvCmd(a app.App) *cobra.Command {
func newEnvCmd() *cobra.Command {
envCmd := &cobra.Command{
Use: "env",
Short: `Manage ksonnet environments`,
......@@ -85,14 +84,14 @@ func newEnvCmd(a app.App) *cobra.Command {
},
}
envCmd.AddCommand(newEnvAddCmd(a))
envCmd.AddCommand(newEnvCurrentCmd(a))
envCmd.AddCommand(newEnvDescribeCmd(a))
envCmd.AddCommand(newEnvListCmd(a))
envCmd.AddCommand(newEnvRmCmd(a))
envCmd.AddCommand(newEnvSetCmd(a))
envCmd.AddCommand(newEnvTargetsCmd(a))
envCmd.AddCommand(newEnvUpdateCmd(a))
envCmd.AddCommand(newEnvAddCmd())
envCmd.AddCommand(newEnvCurrentCmd())
envCmd.AddCommand(newEnvDescribeCmd())
envCmd.AddCommand(newEnvListCmd())
envCmd.AddCommand(newEnvRmCmd())
envCmd.AddCommand(newEnvSetCmd())
envCmd.AddCommand(newEnvTargetsCmd())
envCmd.AddCommand(newEnvUpdateCmd())
return envCmd
......
......@@ -21,7 +21,6 @@ import (
"github.com/spf13/viper"
"github.com/ksonnet/ksonnet/pkg/actions"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/ksonnet/ksonnet/pkg/client"
"github.com/spf13/cobra"
)
......@@ -81,8 +80,8 @@ ks env add my-env --context=dev
ks env add prod --server=https://ksonnet-1.us-west.elb.amazonaws.com`
)
func newEnvAddCmd(a app.App) *cobra.Command {
envClientConfig := client.NewDefaultClientConfig(a)
func newEnvAddCmd() *cobra.Command {
envClientConfig := client.NewDefaultClientConfig()
envAddCmd := &cobra.Command{
Use: "add <env-name>",
......@@ -115,13 +114,13 @@ func newEnvAddCmd(a app.App) *cobra.Command {
isOverride := viper.GetBool(vEnvAddOverride)
m := map[string]interface{}{
actions.OptionApp: a,
actions.OptionEnvName: name,
actions.OptionServer: server,
actions.OptionModule: namespace,
actions.OptionSpecFlag: specFlag,
actions.OptionOverride: isOverride,
}
addGlobalOptions(m)
return runAction(actionEnvAdd, m)
},
......
......@@ -19,7 +19,6 @@ import (
"fmt"
"github.com/ksonnet/ksonnet/pkg/actions"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
......@@ -49,7 +48,7 @@ ks env current
ks env current --unset`
)
func newEnvCurrentCmd(a app.App) *cobra.Command {
func newEnvCurrentCmd() *cobra.Command {
envCurrentCmd := &cobra.Command{
Use: "current [--set <name> | --unset]",
Short: envShortDesc["current"],
......@@ -61,10 +60,10 @@ func newEnvCurrentCmd(a app.App) *cobra.Command {
}
m := map[string]interface{}{
actions.OptionApp: a,
actions.OptionEnvName: viper.GetString(vEnvCurrentSet),
actions.OptionUnset: viper.GetBool(vEnvCurrentUnset),
}
addGlobalOptions(m)
return runAction(actionEnvCurrent, m)
},
......
......@@ -17,12 +17,11 @@ package clicmd
import (
"github.com/ksonnet/ksonnet/pkg/actions"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
func newEnvDescribeCmd(a app.App) *cobra.Command {
func newEnvDescribeCmd() *cobra.Command {
envDescribeCmd := &cobra.Command{
Use: "describe <env>",
Short: "Describe an environment",
......@@ -33,9 +32,9 @@ func newEnvDescribeCmd(a app.App) *cobra.Command {
}
m := map[string]interface{}{
actions.OptionApp: a,
actions.OptionEnvName: args[0],
}
addGlobalOptions(m)
return runAction(actionEnvDescribe, m)
},
......
......@@ -19,7 +19,6 @@ import (
"fmt"
"github.com/ksonnet/ksonnet/pkg/actions"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
......@@ -44,7 +43,7 @@ current ksonnet app. Specifically, this will display the (1) *name*,
`
)
func newEnvListCmd(a app.App) *cobra.Command {
func newEnvListCmd() *cobra.Command {
envListCmd := &cobra.Command{
Use: "list",
Short: envShortDesc["list"],
......@@ -55,9 +54,9 @@ func newEnvListCmd(a app.App) *cobra.Command {
}
m := map[string]interface{}{
actions.OptionApp: a,
actions.OptionOutput: viper.GetString(vEnvListOutput),
}
addGlobalOptions(m)
return runAction(actionEnvList, m)
},
......
......@@ -19,7 +19,6 @@ import (
"fmt"
"github.com/ksonnet/ksonnet/pkg/actions"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/spf13/cobra"