diff --git a/actions/component_rm.go b/actions/component_rm.go new file mode 100644 index 0000000000000000000000000000000000000000..985ad96fd934dad400eb3d1409a3af2e0e556954 --- /dev/null +++ b/actions/component_rm.go @@ -0,0 +1,62 @@ +// 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/component" + "github.com/ksonnet/ksonnet/metadata/app" +) + +// RunComponentRm runs `component list` +func RunComponentRm(m map[string]interface{}) error { + cr, err := NewComponentRm(m) + if err != nil { + return err + } + + return cr.Run() +} + +// ComponentRm create a list of components in a namespace. +type ComponentRm struct { + app app.App + name string + + componentDeleteFn func(app.App, string) error +} + +// NewComponentRm creates an instance of ComponentRm. +func NewComponentRm(m map[string]interface{}) (*ComponentRm, error) { + ol := newOptionLoader(m) + + cr := &ComponentRm{ + app: ol.loadApp(), + name: ol.loadString(OptionComponentName), + + componentDeleteFn: component.Delete, + } + + if ol.err != nil { + return nil, ol.err + } + + return cr, nil +} + +// Run runs the ComponentRm action. +func (cr *ComponentRm) Run() error { + return cr.componentDeleteFn(cr.app, cr.name) +} diff --git a/actions/component_rm_test.go b/actions/component_rm_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7a5b1379c5001f484ef16deaf01b5c87d0a98ef4 --- /dev/null +++ b/actions/component_rm_test.go @@ -0,0 +1,54 @@ +// 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/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestComponentRm(t *testing.T) { + withApp(t, func(appMock *amocks.App) { + name := "component-name" + + var didDelete bool + + deleteFn := func(a app.App, componentName string) error { + assert.Equal(t, componentName, name) + didDelete = true + return nil + } + + in := map[string]interface{}{ + OptionApp: appMock, + OptionComponentName: name, + } + + a, err := NewComponentRm(in) + require.NoError(t, err) + + a.componentDeleteFn = deleteFn + + err = a.Run() + require.NoError(t, err) + + assert.True(t, didDelete) + }) +} diff --git a/actions/env_add.go b/actions/env_add.go index 0e6ee3146a91e7a8dbea19dd74e1ba1ca1b7cc03..d5269c9f75929547b0e8a50f1316b9fd1b302ad9 100644 --- a/actions/env_add.go +++ b/actions/env_add.go @@ -16,14 +16,8 @@ package actions import ( - "github.com/ksonnet/ksonnet/env" "github.com/ksonnet/ksonnet/metadata/app" -) - -const ( - baseLibsonnetFile = "base.libsonnet" - componentsDir = "components" - paramsFileName = "params.libsonnet" + "github.com/ksonnet/ksonnet/pkg/env" ) // RunEnvAdd runs `env add` @@ -79,8 +73,8 @@ func (ea *EnvAdd) Run() error { destination, ea.envName, ea.k8sSpecFlag, - env.DefaultOverrideData(), - env.DefaultParamsData(), + env.DefaultOverrideData, + env.DefaultParamsData, ea.isOverride, ) } diff --git a/actions/env_add_test.go b/actions/env_add_test.go index f5e27512db6a62a743975a4e7f0190d7bbae9679..48fbb1e85efcfe5e121106f8c53f75bf557265e2 100644 --- a/actions/env_add_test.go +++ b/actions/env_add_test.go @@ -18,9 +18,9 @@ package actions import ( "testing" - "github.com/ksonnet/ksonnet/env" "github.com/ksonnet/ksonnet/metadata/app" amocks "github.com/ksonnet/ksonnet/metadata/app/mocks" + "github.com/ksonnet/ksonnet/pkg/env" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/actions/env_rm.go b/actions/env_rm.go index d606aff430afb972db3fda14163b2a6957cd0890..95e1e1573aace6fe871c980a8d7fda2abb3ec872 100644 --- a/actions/env_rm.go +++ b/actions/env_rm.go @@ -16,8 +16,8 @@ package actions import ( - "github.com/ksonnet/ksonnet/env" "github.com/ksonnet/ksonnet/metadata/app" + "github.com/ksonnet/ksonnet/pkg/env" ) // RunEnvRm runs `env rm` diff --git a/actions/env_set.go b/actions/env_set.go index 1eb968cacd571e0bbfb2f0e165a9ccfd8917cb53..e18e1737b07a75ae938cd2acd3eee84dd6ad2d31 100644 --- a/actions/env_set.go +++ b/actions/env_set.go @@ -16,8 +16,8 @@ package actions import ( - "github.com/ksonnet/ksonnet/env" "github.com/ksonnet/ksonnet/metadata/app" + "github.com/ksonnet/ksonnet/pkg/env" ) // EnvSetNamespace is an option for setting a new namespace name. diff --git a/actions/param_set.go b/actions/param_set.go index 8415c3463ddeade3fbfa056b326a87b5b43641fb..2db3d16c830c041647fa8df3d7a673ece2fe7707 100644 --- a/actions/param_set.go +++ b/actions/param_set.go @@ -20,9 +20,9 @@ import ( "strings" "github.com/ksonnet/ksonnet/component" - "github.com/ksonnet/ksonnet/env" "github.com/ksonnet/ksonnet/metadata/app" mp "github.com/ksonnet/ksonnet/metadata/params" + "github.com/ksonnet/ksonnet/pkg/env" "github.com/ksonnet/ksonnet/pkg/params" "github.com/pkg/errors" ) diff --git a/cmd/actions.go b/cmd/actions.go index 699469b8f6ce17098ceae88e9a2e376711418680..9b185e76f8ea1a97fc5f858c9380926d0ed17e72 100644 --- a/cmd/actions.go +++ b/cmd/actions.go @@ -25,6 +25,7 @@ type initName int const ( actionApply initName = iota actionComponentList + actionComponentRm actionDelete actionDiff actionEnvAdd @@ -62,6 +63,7 @@ var ( actionFns = map[initName]actionFn{ actionApply: actions.RunApply, actionComponentList: actions.RunComponentList, + actionComponentRm: actions.RunComponentRm, // actionDelete // actionDiff actionEnvAdd: actions.RunEnvAdd, @@ -102,10 +104,3 @@ func runAction(name initName, args map[string]interface{}) error { return fn(args) } - -var ( - actionMap = map[initName]interface{}{ - actionInit: actions.RunInit, - actionValidate: actions.RunValidate, - } -) diff --git a/cmd/apply.go b/cmd/apply.go index 5bf8b6f054e9f8cc7922a1657f9a5270b55a4c61..cb38bffceb5b37b9be8553a86e8fda0044e4926a 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -32,22 +32,6 @@ var ( ) const ( - - // AnnotationGcTag annotation that triggers - // garbage collection. Objects with value equal to - // command-line flag that are *not* in config will be deleted. - AnnotationGcTag = "kubecfg.ksonnet.io/garbage-collect-tag" - - // AnnotationGcStrategy controls gc logic. Current values: - // `auto` (default if absent) - do garbage collection - // `ignore` - never garbage collect this object - AnnotationGcStrategy = "kubecfg.ksonnet.io/garbage-collect-strategy" - - // GcStrategyAuto is the default automatic gc logic - GcStrategyAuto = "auto" - // GcStrategyIgnore means this object should be ignored by garbage collection - GcStrategyIgnore = "ignore" - applyShortDesc = "Apply local Kubernetes manifests (components) to remote clusters" ) diff --git a/cmd/component_rm.go b/cmd/component_rm.go index c9ea023d852727e4da0e2c7790450eb8c4b3471c..6fd52cea043a641adcfccaa14436916bb43bc3c4 100644 --- a/cmd/component_rm.go +++ b/cmd/component_rm.go @@ -18,7 +18,7 @@ package cmd import ( "fmt" - "github.com/ksonnet/ksonnet/pkg/kubecfg" + "github.com/ksonnet/ksonnet/actions" "github.com/spf13/cobra" ) @@ -30,11 +30,12 @@ var componentRmCmd = &cobra.Command{ return fmt.Errorf("'component rm' takes a single argument, that is the name of the component") } - component := args[0] + m := map[string]interface{}{ + actions.OptionApp: ka, + actions.OptionComponentName: args[0], + } - // TODO: move this to actions - c := kubecfg.NewComponentRmCmd(component) - return c.Run() + return runAction(actionComponentRm, m) }, Long: `Delete a component from the ksonnet application. This is equivalent to deleting the component file in the components directory and cleaning up all component diff --git a/cmd/component_rm_test.go b/cmd/component_rm_test.go index cc5bd9f5d22c2c9bc945f142d42997bc861444ec..ac2a5a550d9631e22ee9cc84961e7ac8483cb37e 100644 --- a/cmd/component_rm_test.go +++ b/cmd/component_rm_test.go @@ -17,22 +17,22 @@ package cmd import ( "testing" + + "github.com/ksonnet/ksonnet/actions" ) func Test_componentRmCmd(t *testing.T) { + cases := []cmdTestCase{ + { + name: "in general", + args: []string{"component", "rm", "name"}, + action: actionComponentRm, + expected: map[string]interface{}{ + actions.OptionApp: ka, + actions.OptionComponentName: "name", + }, + }, + } - // TODO: re-enable when component rm is an action - // cases := []cmdTestCase{ - // { - // name: "in general", - // args: []string{"component", "rm", "name"}, - // action: actionComponentList, - // expected: map[string]interface{}{ - // actions.OptionApp: ka, - // actions.OptionComponentName: "name", - // }, - // }, - // } - - // runTestCmd(t, cases) + runTestCmd(t, cases) } diff --git a/cmd/helpers_test.go b/cmd/helpers_test.go index ecef0ff8f3a349b6d563fce083da45472a0905c4..bc624a9d8108f39ffcf105145a53e18027caa6a0 100644 --- a/cmd/helpers_test.go +++ b/cmd/helpers_test.go @@ -48,20 +48,11 @@ type cmdTestCase struct { expected map[string]interface{} } -func stubCmdOverride() (*map[string]interface{}, func(map[string]interface{}) error) { - var got *map[string]interface{} - - return got, func(m map[string]interface{}) error { - got = &m - return nil - } -} - -type stubCmdOverride2 struct { +type stubCmdOverride struct { got map[string]interface{} } -func (s *stubCmdOverride2) override(m map[string]interface{}) error { +func (s *stubCmdOverride) override(m map[string]interface{}) error { s.got = m return nil } @@ -69,7 +60,7 @@ func (s *stubCmdOverride2) override(m map[string]interface{}) error { func runTestCmd(t *testing.T, cases []cmdTestCase) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - s := stubCmdOverride2{} + s := stubCmdOverride{} withCmd(tc.action, s.override, func() { diff --git a/cmd/root.go b/cmd/root.go index 65bfc192b4a2846c090e97c2b4f12c8d12d87485..b38a0a00f6059b7173b5353dfa38afb79d03c1da 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -28,9 +28,9 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "github.com/ksonnet/ksonnet/env" "github.com/ksonnet/ksonnet/metadata" "github.com/ksonnet/ksonnet/metadata/app" + "github.com/ksonnet/ksonnet/pkg/env" "github.com/ksonnet/ksonnet/pkg/pipeline" "github.com/ksonnet/ksonnet/plugin" str "github.com/ksonnet/ksonnet/strings" @@ -175,15 +175,6 @@ func runPlugin(p plugin.Plugin, args []string) error { return cmd.Run() } -func ksApp() (app.App, error) { - cwd, err := os.Getwd() - if err != nil { - return nil, err - } - - return app.Load(appFs, cwd) -} - func logLevel(verbosity int) log.Level { switch verbosity { case 0: diff --git a/component/component.go b/component/component.go index 6135c7118d9c8bfbda2a72cbab418f6ef65f9e14..1dd3796fe87c29d22a2648e62287f4d5ebafa2ee 100644 --- a/component/component.go +++ b/component/component.go @@ -149,32 +149,6 @@ func isComponentDir(fs afero.Fs, path string) (bool, error) { return false, nil } -// MakePathsByNamespace creates a map of component paths categorized by namespace. -func MakePathsByNamespace(a app.App, env string) (map[Namespace][]string, error) { - paths, err := MakePaths(a, env) - if err != nil { - return nil, err - } - - m := make(map[Namespace][]string) - - for i := range paths { - prefix := a.Root() + "/components/" - if strings.HasSuffix(a.Root(), "/") { - prefix = a.Root() + "components/" - } - path := strings.TrimPrefix(paths[i], prefix) - ns, _ := ExtractNamespacedComponent(a, path) - if _, ok := m[ns]; !ok { - m[ns] = make([]string, 0) - } - - m[ns] = append(m[ns], paths[i]) - } - - return m, nil -} - // MakePaths creates a slice of component paths func MakePaths(a app.App, env string) ([]string, error) { cpl, err := newComponentPathLocator(a, env) diff --git a/component/delete.go b/component/delete.go new file mode 100644 index 0000000000000000000000000000000000000000..d858a715a962bc1c46afab8a2eeb94dfa097a301 --- /dev/null +++ b/component/delete.go @@ -0,0 +1,122 @@ +// 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 component + +import ( + "path/filepath" + + "github.com/ksonnet/ksonnet/metadata/app" + param "github.com/ksonnet/ksonnet/metadata/params" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + "github.com/spf13/afero" +) + +// Delete deletes the component file and all references. +// Write operations will happen at the end to minimal-ize failures that leave +// the directory structure in a half-finished state. +func Delete(a app.App, name string) error { + log.Debugf("deleting component %s", name) + componentPath, err := Path(a, name) + if err != nil { + return err + } + + ns, _ := ExtractNamespacedComponent(a, name) + + // Build the new component/params.libsonnet file. + componentParamsFile, err := afero.ReadFile(a.Fs(), ns.ParamsPath()) + if err != nil { + return err + } + componentJsonnet, err := param.DeleteComponent(name, string(componentParamsFile)) + if err != nil { + return err + } + + // Build the new environment/<env>/params.libsonnet files. + // environment name -> jsonnet + envParams := make(map[string]string) + envs, err := a.Environments() + if err != nil { + return err + } + for envName, env := range envs { + var updated string + updated, err = collectEnvParams(a, env, name, envName) + if err != nil { + return err + } + + envParams[envName] = updated + } + + // + // Delete the component references. + // + log.Infof("Removing component parameter references ...") + + // Remove the references in component/params.libsonnet. + log.Debugf("... deleting references in %s", ns.ParamsPath()) + err = afero.WriteFile(a.Fs(), ns.ParamsPath(), []byte(componentJsonnet), defaultFilePermissions) + if err != nil { + return err + } + + if err = updateEnvParam(a, envs, envParams); err != nil { + return errors.Wrap(err, "writing environment params") + } + + // + // Delete the component file in components/. + // + log.Infof("Deleting component '%s' at path '%s'", name, componentPath) + if err := a.Fs().Remove(componentPath); err != nil { + return err + } + + // TODO: Remove, + // references in main.jsonnet. + // component references in other component files (feature does not yet exist). + log.Infof("Successfully deleted component '%s'", name) + return nil +} + +// collectEnvParams collects environment params in +func collectEnvParams(a app.App, env *app.EnvironmentSpec, componentName, envName string) (string, error) { + log.Debugf("collecting params for environment %s", envName) + path := filepath.Join(a.Root(), "environments", envName, "params.libsonnet") + var envParamsFile []byte + envParamsFile, err := afero.ReadFile(a.Fs(), path) + if err != nil { + return "", err + } + return param.DeleteEnvironmentComponent(componentName, string(envParamsFile)) +} + +/// updateEnvParam removes the component references in each environment's +// paramss.libsonnet. +func updateEnvParam(a app.App, envs app.EnvironmentSpecs, envParams map[string]string) error { + for envName := range envs { + path := filepath.Join(a.Root(), "environments", envName, "params.libsonnet") + log.Debugf("... deleting references in %s", path) + if err := afero.WriteFile(a.Fs(), path, []byte(envParams[envName]), app.DefaultFilePermissions); err != nil { + return errors.Wrapf(err, "writing params for environment %q", envName) + } + } + + return nil +} diff --git a/env/env_test.go b/component/delete_test.go similarity index 51% rename from env/env_test.go rename to component/delete_test.go index d0b165881d72400726e3d0684b0c0f2194d8a43f..a16f70d38d4fbda9d94043d5b3b035a90cf1ab04 100644 --- a/env/env_test.go +++ b/component/delete_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 The kubecfg authors +// Copyright 2018 The ksonnet authors // // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,42 +13,43 @@ // See the License for the specific language governing permissions and // limitations under the License. -package env +package component import ( + "path/filepath" "testing" "github.com/ksonnet/ksonnet/metadata/app" "github.com/ksonnet/ksonnet/metadata/app/mocks" + "github.com/ksonnet/ksonnet/pkg/util/test" "github.com/spf13/afero" "github.com/stretchr/testify/require" ) -func TestList(t *testing.T) { - withEnv(t, func(appMock *mocks.App, fs afero.Fs) { - specEnvs := app.EnvironmentSpecs{ - "default": &app.EnvironmentSpec{ - Path: "default", - Destination: &app.EnvironmentDestinationSpec{ - Namespace: "default", - Server: "http://example.com", - }, - KubernetesVersion: "v1.8.7", - }, +func TestDelete(t *testing.T) { + test.WithApp(t, "/app", func(a *mocks.App, fs afero.Fs) { + test.StageDir(t, fs, "delete", "/app") + + envs := app.EnvironmentSpecs{ + "default": &app.EnvironmentSpec{}, } - appMock.On("Environments").Return(specEnvs, nil) + a.On("Environments").Return(envs, nil) - envs, err := List(appMock) + err := Delete(a, "guestbook-ui") require.NoError(t, err) - expected := map[string]Env{ - "default": Env{ - KubernetesVersion: "v1.8.7", - Name: "default", - Destination: NewDestination("http://example.com", "default"), - }, - } - - require.Equal(t, expected, envs) + test.AssertNotExists(t, fs, filepath.Join("/app", "components", "guestbook-ui.jsonnet")) + test.AssertContents( + t, + fs, + "delete-params.libsonnet", + filepath.Join("/app", "components", "params.libsonnet"), + ) + test.AssertContents( + t, + fs, + "delete-env-params.libsonnet", + filepath.Join("/app", "environments", "default", "params.libsonnet"), + ) }) } diff --git a/component/testdata/delete-env-params.libsonnet b/component/testdata/delete-env-params.libsonnet new file mode 100644 index 0000000000000000000000000000000000000000..796f6ac9a45714184b3a8d76202e6b85a4d2f5fa --- /dev/null +++ b/component/testdata/delete-env-params.libsonnet @@ -0,0 +1,5 @@ +local params = import "../../components/params.libsonnet"; +params { + components +: { + }, +} diff --git a/component/testdata/delete-params.libsonnet b/component/testdata/delete-params.libsonnet new file mode 100644 index 0000000000000000000000000000000000000000..4fe2a83791f1bcad9e4fb92922d9725b60eaa0f7 --- /dev/null +++ b/component/testdata/delete-params.libsonnet @@ -0,0 +1,10 @@ +{ + global: { + // User-defined global parameters; accessible to all component and environments, Ex: + // replicas: 4, + }, + components: { + // Component-level parameters, defined initially from 'ks prototype use ...' + // Each object below should correspond to a component in the components/ directory + }, +} diff --git a/component/testdata/delete/components/guestbook-ui.jsonnet b/component/testdata/delete/components/guestbook-ui.jsonnet new file mode 100644 index 0000000000000000000000000000000000000000..5e1c81978e3970a3fdbfcfb48719bc5940d10e34 --- /dev/null +++ b/component/testdata/delete/components/guestbook-ui.jsonnet @@ -0,0 +1,29 @@ +local env = std.extVar("__ksonnet/environments"); +local params = std.extVar("__ksonnet/params").components["guestbook-ui"]; +local k = import "k.libsonnet"; +local deployment = k.apps.v1beta1.deployment; +local container = k.apps.v1beta1.deployment.mixin.spec.template.spec.containersType; +local containerPort = container.portsType; +local service = k.core.v1.service; +local servicePort = k.core.v1.service.mixin.spec.portsType; + +local targetPort = params.containerPort; +local labels = {app: params.name}; + +local appService = service + .new( + params.name, + labels, + servicePort.new(params.servicePort, targetPort)) + .withType(params.type); + +local appDeployment = deployment + .new( + params.name, + params.replicas, + container + .new(params.name, params.image) + .withPorts(containerPort.new(targetPort)), + labels); + +k.core.v1.list.new([appService, appDeployment]) \ No newline at end of file diff --git a/component/testdata/delete/components/params.libsonnet b/component/testdata/delete/components/params.libsonnet new file mode 100644 index 0000000000000000000000000000000000000000..4fe74232ef21fb682fa334856eba5ae4116aed91 --- /dev/null +++ b/component/testdata/delete/components/params.libsonnet @@ -0,0 +1,19 @@ +{ + global: { + // User-defined global parameters; accessible to all component and environments, Ex: + // replicas: 4, + }, + components: { + // Component-level parameters, defined initially from 'ks prototype use ...' + // Each object below should correspond to a component in the components/ directory + "guestbook-ui": { + containerPort: 80, + image: "gcr.io/heptio-images/ks-guestbook-demo:0.1", + name: "guiroot", + replicas: 1, + servicePort: 80, + type: "ClusterIP", + obj: {a: "b"}, + }, + }, +} diff --git a/component/testdata/delete/environments/default/params.libsonnet b/component/testdata/delete/environments/default/params.libsonnet new file mode 100644 index 0000000000000000000000000000000000000000..bc6b4dc093afc1d41c9006f1170cfa0fcce8c9b4 --- /dev/null +++ b/component/testdata/delete/environments/default/params.libsonnet @@ -0,0 +1,8 @@ +local params = import "../../components/params.libsonnet"; +params { + components +: { + "guestbook-ui" +: { + name: "guestbook-dev", + }, + }, +} diff --git a/e2e/component_test.go b/e2e/component_test.go index c183cc76e1ee37e4ae91f1e89c5a6ae77ba63553..0281b33e1c80584d9039d9a8b980d78a052fcef7 100644 --- a/e2e/component_test.go +++ b/e2e/component_test.go @@ -55,7 +55,7 @@ var _ = Describe("ks component", func() { Describe("rm", func() { It("removes a component", func() { - o := a.runKs("component", "rm", "guestbook-ui") + o := a.runKs("component", "rm", "guestbook-ui", "-v") assertExitStatus(o, 0) o = a.componentList() diff --git a/env/env.go b/env/env.go deleted file mode 100644 index 6a168e45de7f295cd5bd5ae3961a1838ef03eaac..0000000000000000000000000000000000000000 --- a/env/env.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2018 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 env - -import ( - "github.com/ksonnet/ksonnet/metadata/app" -) - -const ( - // primary environment files. - envFileName = "main.jsonnet" - paramsFileName = "params.libsonnet" - - // envRoot is the name for the environment root. - envRoot = "environments" -) - -// Env represents a ksonnet environment. -type Env struct { - // Name is the environment name. - Name string - // KubernetesVersion is the version of Kubernetes for this environment. - KubernetesVersion string - // Destination is the cluster destination for this environment. - Destination Destination - // Targets are the component namespaces that will be installed. - Targets []string -} - -func envFromSpec(name string, envSpec *app.EnvironmentSpec) *Env { - return &Env{ - Name: name, - KubernetesVersion: envSpec.KubernetesVersion, - Destination: NewDestination(envSpec.Destination.Server, envSpec.Destination.Namespace), - Targets: envSpec.Targets, - } -} - -// List lists all environments for the current ksonnet application. -func List(ksApp app.App) (map[string]Env, error) { - envs := make(map[string]Env) - - specs, err := ksApp.Environments() - if err != nil { - return nil, err - } - - for name, spec := range specs { - env := envFromSpec(name, spec) - envs[name] = *env - } - - return envs, nil -} - -// Retrieve retrieves an environment by name. -func Retrieve(ksApp app.App, name string) (*Env, error) { - envSpec, err := ksApp.Environment(name) - if err != nil { - return nil, err - } - - return envFromSpec(name, envSpec), nil -} diff --git a/metadata/component.go b/metadata/component.go index 962e7c6265e43c5eb6d2ee8b5441de13257bb57a..de41c5b89e115ee08a99072bb0985bf37343636b 100644 --- a/metadata/component.go +++ b/metadata/component.go @@ -20,6 +20,7 @@ import ( "path" "github.com/ksonnet/ksonnet/component" + "github.com/ksonnet/ksonnet/metadata/app" param "github.com/ksonnet/ksonnet/metadata/params" "github.com/ksonnet/ksonnet/prototype" str "github.com/ksonnet/ksonnet/strings" @@ -115,7 +116,7 @@ func (m *manager) DeleteComponent(name string) error { // Build the new environment/<env>/params.libsonnet files. // environment name -> jsonnet envJsonnets := make(map[string]string) - envs, err := m.GetEnvironments() + envs, err := ksApp.Environments() if err != nil { return err } @@ -139,7 +140,7 @@ func (m *manager) DeleteComponent(name string) error { // Remove the references in component/params.libsonnet. log.Debugf("... deleting references in %s", m.componentParamsPath) - err = afero.WriteFile(m.appFS, ns.ParamsPath(), []byte(componentJsonnet), defaultFilePermissions) + err = afero.WriteFile(m.appFS, ns.ParamsPath(), []byte(componentJsonnet), app.DefaultFilePermissions) if err != nil { return err } @@ -148,7 +149,7 @@ func (m *manager) DeleteComponent(name string) error { for _, env := range envs { path := str.AppendToPath(m.environmentsPath, env.Name, paramsFileName) log.Debugf("... deleting references in %s", path) - err = afero.WriteFile(m.appFS, path, []byte(envJsonnets[env.Name]), defaultFilePermissions) + err = afero.WriteFile(m.appFS, path, []byte(envJsonnets[env.Name]), app.DefaultFilePermissions) if err != nil { return err } @@ -234,5 +235,5 @@ func (m *manager) SetComponentParams(path string, params param.Params) error { return err } - return afero.WriteFile(m.appFS, paramsPath, []byte(jsonnet), defaultFilePermissions) + return afero.WriteFile(m.appFS, paramsPath, []byte(jsonnet), app.DefaultFilePermissions) } diff --git a/metadata/environment.go b/metadata/environment.go index 22b9bc872b08dabe707ec0c5c710164f74ca7f4c..12b39b06755fedcbcc6e94d37a56d790b0377c22 100644 --- a/metadata/environment.go +++ b/metadata/environment.go @@ -16,23 +16,13 @@ package metadata import ( - "bytes" - "fmt" - "runtime/debug" - - "github.com/ksonnet/ksonnet/env" - "github.com/ksonnet/ksonnet/metadata/lib" + "github.com/ksonnet/ksonnet/pkg/env" str "github.com/ksonnet/ksonnet/strings" - "github.com/pkg/errors" - - log "github.com/sirupsen/logrus" param "github.com/ksonnet/ksonnet/metadata/params" ) const ( - defaultEnvName = "default" - // primary environment files envFileName = "main.jsonnet" paramsFileName = "params.libsonnet" @@ -43,66 +33,15 @@ var ( envCreate = env.Create ) -func (m *manager) CreateEnvironment(name, server, namespace, k8sSpecFlag string) error { - debug.PrintStack() - return errors.Errorf("deprecated") - // a, err := m.App() - // if err != nil { - // return err - // } - - // config := env.CreateConfig{ - // App: a, - // Destination: env.NewDestination(server, namespace), - // K8sSpecFlag: k8sSpecFlag, - // Name: name, - - // OverrideData: m.generateOverrideData(), - // ParamsData: m.generateParamsData(), - // } - - // return envCreate(config) - -} - -func (m *manager) DeleteEnvironment(name string) error { - a, err := m.App() - if err != nil { - return err - } - - // TODO: move this to actions - return env.Delete(a, name, false) -} - -func (m *manager) GetEnvironments() (map[string]env.Env, error) { - a, err := m.App() - if err != nil { - return nil, err - } - - log.Debug("Retrieving all environments") - return env.List(a) -} - -func (m *manager) GetEnvironment(name string) (*env.Env, error) { - a, err := m.App() - if err != nil { - return nil, err - } - - return env.Retrieve(a, name) -} - -func (m *manager) SetEnvironment(from, to string) error { - a, err := m.App() - if err != nil { - return err - } +// func (m *manager) DeleteEnvironment(name string) error { +// a, err := m.App() +// if err != nil { +// return err +// } - // TODO: move this to an action - return env.Rename(a, from, to, false) -} +// // TODO: move this to actions +// return env.Delete(a, name, false) +// } func (m *manager) GetEnvironmentParams(name, nsName string) (map[string]param.Params, error) { a, err := m.App() @@ -155,32 +94,3 @@ func (m *manager) getLibPath(envName string) (string, error) { return a.LibPath(envName) } - -func (m *manager) generateOverrideData() []byte { - var buf bytes.Buffer - buf.WriteString(fmt.Sprintf("local base = import \"%s\";\n", baseLibsonnetFile)) - buf.WriteString(fmt.Sprintf("local k = import \"%s\";\n\n", lib.ExtensionsLibFilename)) - buf.WriteString("base + {\n") - buf.WriteString(" // Insert user-specified overrides here. For example if a component is named \"nginx-deployment\", you might have something like:\n") - buf.WriteString(" // \"nginx-deployment\"+: k.deployment.mixin.metadata.labels({foo: \"bar\"})\n") - buf.WriteString("}\n") - return buf.Bytes() -} - -func (m *manager) generateParamsData() []byte { - const ( - relComponentParamsPath = "../../" + componentsDir + "/" + paramsFileName - ) - - return []byte(`local params = import "` + relComponentParamsPath + `"; -params + { - components +: { - // Insert component parameter overrides here. Ex: - // guestbook +: { - // name: "guestbook-dev", - // replicas: params.global.replicas, - // }, - }, -} -`) -} diff --git a/metadata/interface.go b/metadata/interface.go index 34ddf47089477d75c090df179dd9cd5aed7ae704..53d24c08fc525ec884569447dbab4e51020aca12 100644 --- a/metadata/interface.go +++ b/metadata/interface.go @@ -16,20 +16,14 @@ package metadata import ( - "os" - "github.com/ksonnet/ksonnet/component" - "github.com/ksonnet/ksonnet/env" "github.com/ksonnet/ksonnet/metadata/app" param "github.com/ksonnet/ksonnet/metadata/params" - "github.com/ksonnet/ksonnet/pkg/registry" "github.com/ksonnet/ksonnet/prototype" "github.com/spf13/afero" ) var appFS afero.Fs -var defaultFolderPermissions = os.FileMode(0755) -var defaultFilePermissions = os.FileMode(0644) // Manager abstracts over a ksonnet application's metadata, allowing users to do // things like: create and delete environments; search for prototypes; vendor @@ -59,12 +53,7 @@ type Manager interface { SetEnvironmentParams(env, component string, params param.Params) error // Environment API. - // CreateEnvironment(name, uri, namespace, spec string) error - DeleteEnvironment(name string) error - GetEnvironments() (map[string]env.Env, error) - GetEnvironment(name string) (*env.Env, error) - SetEnvironment(name, desiredName string) error - GetDestination(envName string) (env.Destination, error) + // DeleteEnvironment(name string) error } // Find will recursively search the current directory and its parents for a @@ -74,27 +63,6 @@ func Find(path string) (Manager, error) { return findManager(path, afero.NewOsFs()) } -// Init will generate the directory tree for a ksonnet project. -func Init(name, rootPath string, k8sSpecFlag, serverURI, namespace *string) (Manager, error) { - // Generate `incubator` registry. We do this before before creating - // directory tree, in case the network call fails. - const ( - defaultIncubatorRegName = "incubator" - defaultIncubatorURI = "github.com/ksonnet/parts/tree/master/" + defaultIncubatorRegName - ) - - gh, err := registry.NewGitHub(&app.RegistryRefSpec{ - Name: "incubator", - Protocol: registry.ProtocolGitHub, - URI: defaultIncubatorURI, - }) - if err != nil { - return nil, err - } - - return initManager(name, rootPath, k8sSpecFlag, serverURI, namespace, gh, appFS) -} - func init() { appFS = afero.NewOsFs() } diff --git a/metadata/manager.go b/metadata/manager.go index bf5b888020123e2058e32491b9ab63fb0e58beca..84af2d3332e96f4593c4fea8b609214df1249067 100644 --- a/metadata/manager.go +++ b/metadata/manager.go @@ -21,13 +21,9 @@ import ( "path" "path/filepath" - "github.com/ksonnet/ksonnet/component" - "github.com/ksonnet/ksonnet/env" "github.com/ksonnet/ksonnet/metadata/app" - "github.com/ksonnet/ksonnet/pkg/registry" str "github.com/ksonnet/ksonnet/strings" "github.com/pkg/errors" - log "github.com/sirupsen/logrus" "github.com/spf13/afero" ) @@ -115,55 +111,6 @@ func findManager(p string, appFS afero.Fs) (*manager, error) { } } -func initManager(name, rootPath string, k8sSpecFlag, serverURI, namespace *string, incubatorReg registry.Registry, appFS afero.Fs) (*manager, error) { - m, err := newManager(rootPath, appFS) - if err != nil { - return nil, errors.Wrap(err, "create manager") - } - - // Retrieve `registry.yaml`. - registryYAMLData, err := generateRegistryYAMLData(incubatorReg) - if err != nil { - return nil, err - } - - // Generate data for `app.yaml`. - appYAMLData, err := generateAppYAMLData(name, incubatorReg.MakeRegistryRefSpec()) - if err != nil { - return nil, err - } - - // Generate data for `base.libsonnet`. - baseLibData := genBaseLibsonnetContent() - - // Initialize directory structure. - if err := m.createAppDirTree(name, appYAMLData, baseLibData, incubatorReg); err != nil { - return nil, err - } - - // Initialize user dir structure. - if err := m.createUserDirTree(); err != nil { - return nil, errorOnCreateFailure(name, err) - } - - // Initialize environment, and cache specification data. - if serverURI != nil { - err := m.CreateEnvironment(defaultEnvName, *serverURI, *namespace, *k8sSpecFlag) - if err != nil { - return nil, errorOnCreateFailure(name, err) - } - } - - // Write out `incubator` registry spec. - registryPath := m.registryPath(incubatorReg) - err = afero.WriteFile(m.appFS, registryPath, registryYAMLData, defaultFilePermissions) - if err != nil { - return nil, errorOnCreateFailure(name, err) - } - - return m, nil -} - func newManager(rootPath string, appFS afero.Fs) (*manager, error) { usr, err := user.Current() if err != nil { @@ -206,126 +153,3 @@ func (m *manager) App() (app.App, error) { func (m *manager) LibPaths() (envPath, vendorPath string) { return m.environmentsPath, m.vendorPath } - -func (m *manager) GetDestination(envName string) (env.Destination, error) { - appEnv, err := m.GetEnvironment(envName) - if err != nil { - return env.Destination{}, err - } - - return appEnv.Destination, nil -} - -func (m *manager) createUserDirTree() error { - dirPaths := []string{ - m.userKsonnetRootPath, - m.pkgSrcCachePath, - } - - for _, p := range dirPaths { - if err := m.appFS.MkdirAll(p, defaultFolderPermissions); err != nil { - return err - } - } - - return nil -} - -func (m *manager) createAppDirTree(name string, appYAMLData, baseLibData []byte, gh registry.Registry) error { - exists, err := afero.DirExists(m.appFS, m.rootPath) - if err != nil { - return fmt.Errorf("Could not check existence of directory '%s':\n%v", m.rootPath, err) - } else if exists { - return fmt.Errorf("Could not create app; directory '%s' already exists", m.rootPath) - } - - dirPaths := []string{ - m.rootPath, - m.ksonnetPath, - m.registriesPath, - m.libPath, - m.componentsPath, - m.environmentsPath, - m.vendorPath, - m.registryDir(gh), - } - - for _, p := range dirPaths { - log.Debugf("Creating directory '%s'", p) - if err := m.appFS.MkdirAll(p, defaultFolderPermissions); err != nil { - return errorOnCreateFailure(name, err) - } - } - - filePaths := []struct { - path string - content []byte - }{ - { - m.componentParamsPath, - component.GenParamsContent(), - }, - { - m.baseLibsonnetPath, - genBaseLibsonnetContent(), - }, - { - m.appYAMLPath, - appYAMLData, - }, - { - m.baseLibsonnetPath, - baseLibData, - }, - } - - for _, f := range filePaths { - log.Debugf("Creating file '%s'", f.path) - if err := afero.WriteFile(m.appFS, f.path, f.content, defaultFilePermissions); err != nil { - return err - } - } - - return nil -} - -func generateRegistryYAMLData(incubatorReg registry.Registry) ([]byte, error) { - regSpec, err := incubatorReg.FetchRegistrySpec() - if err != nil { - return nil, err - } - - return regSpec.Marshal() -} - -func generateAppYAMLData(name string, refs ...*app.RegistryRefSpec) ([]byte, error) { - content := app.Spec{ - APIVersion: app.DefaultAPIVersion, - Kind: app.Kind, - Name: name, - Version: app.DefaultVersion, - Registries: app.RegistryRefSpecs{}, - Environments: app.EnvironmentSpecs{}, - } - - for _, ref := range refs { - err := content.AddRegistryRef(ref) - if err != nil { - return nil, err - } - } - - return content.Marshal() -} - -func genBaseLibsonnetContent() []byte { - return []byte(`local components = std.extVar("` + ComponentsExtCodeKey + `"); -components + { - // Insert user-specified overrides here. -} -`) -} - -func errorOnCreateFailure(appName string, err error) error { - return fmt.Errorf("%s\nTo undo this simply delete directory '%s' and re-run `ks init`.\nIf the error persists, try using flag '--context' to set a different context or run `ks init --help` for more options", err, appName) -} diff --git a/pkg/appinit/init.go b/pkg/appinit/init.go index 69345354b84e9423a67795893136ad92b3964169..c2006a1844114d50931bcd12c6239d9d86f8fcdd 100644 --- a/pkg/appinit/init.go +++ b/pkg/appinit/init.go @@ -19,8 +19,8 @@ import ( "path/filepath" "github.com/ksonnet/ksonnet/component" - "github.com/ksonnet/ksonnet/env" "github.com/ksonnet/ksonnet/metadata/app" + "github.com/ksonnet/ksonnet/pkg/env" "github.com/ksonnet/ksonnet/pkg/registry" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -78,8 +78,8 @@ func (i *initApp) Run() error { d, env.DefaultEnvName, i.k8sSpecFlag, - env.DefaultOverrideData(), - env.DefaultParamsData(), + env.DefaultOverrideData, + env.DefaultParamsData, false, ) @@ -193,7 +193,7 @@ func (i *initApp) createAppDirTree() error { }, { filepath.Join(i.rootPath, "environments", "base.libsonnet"), - env.DefaultBaseData(), + env.DefaultBaseData, }, { filepath.Join(i.rootPath, "app.yaml"), diff --git a/env/create.go b/pkg/env/create.go similarity index 94% rename from env/create.go rename to pkg/env/create.go index fe891eb3cf6dc930ac76e6ef1bcf92271aad0bf5..29555e563cc3c5cf14375d5c873b45d54a7da1a3 100644 --- a/env/create.go +++ b/pkg/env/create.go @@ -33,17 +33,6 @@ const ( DefaultEnvName = "default" ) -// CreateConfig is configuration for creating an environment. -type CreateConfig struct { - App app.App - Destination Destination - K8sSpecFlag string - Name string - - OverrideData []byte - ParamsData []byte -} - // Create creates a new environment for the project. func Create(a app.App, d Destination, name, k8sSpecFlag string, overrideData, paramsData []byte, isOverride bool) error { c, err := newCreator(a, d, name, k8sSpecFlag, overrideData, paramsData, isOverride) diff --git a/env/create_test.go b/pkg/env/create_test.go similarity index 100% rename from env/create_test.go rename to pkg/env/create_test.go diff --git a/env/data.go b/pkg/env/data.go similarity index 59% rename from env/data.go rename to pkg/env/data.go index afa4fb82050efefaa498cf2ce96cd7cba96acc45..447c0cad1b722238b8ea222d438299852218a615 100644 --- a/env/data.go +++ b/pkg/env/data.go @@ -15,35 +15,25 @@ package env -import ( - "bytes" - "fmt" -) - const ( // ComponentsExtCodeKey is the ExtCode key for component imports ComponentsExtCodeKey = "__ksonnet/components" + + relComponentParamsPath = "../../components/params.libsonnet" ) // DefaultOverrideData generates the contents for an environment's `main.jsonnet`. -func DefaultOverrideData() []byte { - var buf bytes.Buffer - buf.WriteString(fmt.Sprintf("local base = import \"%s\";\n", "base.libsonnet")) - buf.WriteString(fmt.Sprintf("local k = import \"%s\";\n\n", "k.libsonnet")) - buf.WriteString("base + {\n") - buf.WriteString(" // Insert user-specified overrides here. For example if a component is named \"nginx-deployment\", you might have something like:\n") - buf.WriteString(" // \"nginx-deployment\"+: k.deployment.mixin.metadata.labels({foo: \"bar\"})\n") - buf.WriteString("}\n") - return buf.Bytes() +var DefaultOverrideData = []byte(`local base = import "base.libsonnet"; +local k = import "k.libsonnet" + +base + { + // Insert user-specified overrides here. For example if a component is named \"nginx-deployment\", you might have something like:\n") + // "nginx-deployment"+: k.deployment.mixin.metadata.labels({foo: "bar"}) } +`) // DefaultParamsData generates the contents for an environment's `params.libsonnet` -func DefaultParamsData() []byte { - const ( - relComponentParamsPath = "../../components/params.libsonnet" - ) - - return []byte(`local params = import "` + relComponentParamsPath + `"; +var DefaultParamsData = []byte(`local params = import "` + relComponentParamsPath + `"; params + { components +: { // Insert component parameter overrides here. Ex: @@ -54,13 +44,10 @@ params + { }, } `) -} // DefaultBaseData generates environment `base.libsonnet`. -func DefaultBaseData() []byte { - return []byte(`local components = std.extVar("` + ComponentsExtCodeKey + `"); +var DefaultBaseData = []byte(`local components = std.extVar("` + ComponentsExtCodeKey + `"); components + { // Insert user-specified overrides here. } `) -} diff --git a/env/delete.go b/pkg/env/delete.go similarity index 100% rename from env/delete.go rename to pkg/env/delete.go diff --git a/env/delete_test.go b/pkg/env/delete_test.go similarity index 100% rename from env/delete_test.go rename to pkg/env/delete_test.go diff --git a/env/destination.go b/pkg/env/destination.go similarity index 100% rename from env/destination.go rename to pkg/env/destination.go diff --git a/pkg/env/env.go b/pkg/env/env.go new file mode 100644 index 0000000000000000000000000000000000000000..419eb6d48c34f7a86803fdb8d9f739ecf35eb724 --- /dev/null +++ b/pkg/env/env.go @@ -0,0 +1,25 @@ +// Copyright 2018 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 env + +const ( + // primary environment files. + envFileName = "main.jsonnet" + paramsFileName = "params.libsonnet" + + // envRoot is the name for the environment root. + envRoot = "environments" +) diff --git a/pkg/env/env_test.go b/pkg/env/env_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7d2a70aa4f7b8feb3f35dd34d8f66d47d786ab72 --- /dev/null +++ b/pkg/env/env_test.go @@ -0,0 +1,16 @@ +// Copyright 2018 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 env diff --git a/env/params.go b/pkg/env/params.go similarity index 100% rename from env/params.go rename to pkg/env/params.go diff --git a/env/params_test.go b/pkg/env/params_test.go similarity index 100% rename from env/params_test.go rename to pkg/env/params_test.go diff --git a/env/rename.go b/pkg/env/rename.go similarity index 82% rename from env/rename.go rename to pkg/env/rename.go index 9f95e6bd40a161d278f295e3b7ee084f508e1853..e480238876a8ef708253bce8a233fbfa7f52d447 100644 --- a/env/rename.go +++ b/pkg/env/rename.go @@ -98,39 +98,6 @@ func envExists(ksApp app.App, name string) (bool, error) { return afero.Exists(ksApp.Fs(), path) } -func moveDir(fs afero.Fs, src, dest string) error { - exists, err := afero.DirExists(fs, dest) - if err != nil { - return err - } - - if !exists { - if err = fs.MkdirAll(dest, app.DefaultFolderPermissions); err != nil { - return errors.Wrapf(err, "unable to create destination %q", dest) - } - } - - fis, err := afero.ReadDir(fs, src) - if err != nil { - return err - } - - for _, fi := range fis { - if fi.IsDir() && fi.Name() != ".metadata" { - continue - } - - srcPath := filepath.Join(src, fi.Name()) - destPath := filepath.Join(dest, fi.Name()) - - if err = fs.Rename(srcPath, destPath); err != nil { - return err - } - } - - return nil -} - func envPath(ksApp app.App, name string, subPath ...string) string { return filepath.Join(append([]string{ksApp.Root(), envRoot, name}, subPath...)...) } diff --git a/env/rename_test.go b/pkg/env/rename_test.go similarity index 100% rename from env/rename_test.go rename to pkg/env/rename_test.go diff --git a/env/testdata/app.yaml b/pkg/env/testdata/app.yaml similarity index 100% rename from env/testdata/app.yaml rename to pkg/env/testdata/app.yaml diff --git a/env/testdata/component-params.libsonnet b/pkg/env/testdata/component-params.libsonnet similarity index 100% rename from env/testdata/component-params.libsonnet rename to pkg/env/testdata/component-params.libsonnet diff --git a/env/testdata/main.jsonnet b/pkg/env/testdata/main.jsonnet similarity index 100% rename from env/testdata/main.jsonnet rename to pkg/env/testdata/main.jsonnet diff --git a/env/testdata/params.libsonnet b/pkg/env/testdata/params.libsonnet similarity index 100% rename from env/testdata/params.libsonnet rename to pkg/env/testdata/params.libsonnet diff --git a/env/testdata/updated-params.libsonnet b/pkg/env/testdata/updated-params.libsonnet similarity index 100% rename from env/testdata/updated-params.libsonnet rename to pkg/env/testdata/updated-params.libsonnet diff --git a/env/util_test.go b/pkg/env/util_test.go similarity index 100% rename from env/util_test.go rename to pkg/env/util_test.go diff --git a/pkg/kubecfg/apply.go b/pkg/kubecfg/apply.go deleted file mode 100644 index 75abd2bc73ee83c4ebc93565c7d1d0f15a99440d..0000000000000000000000000000000000000000 --- a/pkg/kubecfg/apply.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2018 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 kubecfg - -import ( - "encoding/json" - "fmt" - "sort" - - "github.com/ksonnet/ksonnet/client" - "github.com/ksonnet/ksonnet/metadata/app" - "github.com/ksonnet/ksonnet/utils" - log "github.com/sirupsen/logrus" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - kdiff "k8s.io/apimachinery/pkg/util/diff" - "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/client-go/discovery" - "k8s.io/client-go/dynamic" -) - -const ( - // AnnotationGcTag annotation that triggers - // garbage collection. Objects with value equal to - // command-line flag that are *not* in config will be deleted. - AnnotationGcTag = "kubecfg.ksonnet.io/garbage-collect-tag" - - // AnnotationGcStrategy controls gc logic. Current values: - // `auto` (default if absent) - do garbage collection - // `ignore` - never garbage collect this object - AnnotationGcStrategy = "kubecfg.ksonnet.io/garbage-collect-strategy" - - // GcStrategyAuto is the default automatic gc logic - GcStrategyAuto = "auto" - // GcStrategyIgnore means this object should be ignored by garbage collection - GcStrategyIgnore = "ignore" -) - -// ApplyCmd represents the apply subcommand -type ApplyCmd struct { - App app.App - ClientConfig *client.Config - Env string - Create bool - GcTag string - SkipGc bool - DryRun bool -} - -// Run applies the components to the designated environment cluster. -func (c *ApplyCmd) Run(apiObjects []*unstructured.Unstructured, wd string) error { - clientPool, discovery, namespace, err := c.ClientConfig.RestClient(c.App, &c.Env) - if err != nil { - return err - } - - dryRunText := "" - if c.DryRun { - dryRunText = " (dry-run)" - } - - sort.Sort(utils.DependencyOrder(apiObjects)) - - seenUids := sets.NewString() - - for _, obj := range apiObjects { - if c.GcTag != "" { - utils.SetMetaDataAnnotation(obj, AnnotationGcTag, c.GcTag) - } - - desc := fmt.Sprintf("%s %s", utils.ResourceNameFor(discovery, obj), utils.FqName(obj)) - log.Info("Updating ", desc, dryRunText) - - rc, err := utils.ClientForResource(clientPool, discovery, obj, namespace) - if err != nil { - return err - } - - asPatch, err := json.Marshal(obj) - if err != nil { - return err - } - var newobj metav1.Object - if !c.DryRun { - newobj, err = rc.Patch(obj.GetName(), types.MergePatchType, asPatch) - log.Debugf("Patch(%s) returned (%v, %v)", obj.GetName(), newobj, err) - } else { - newobj, err = rc.Get(obj.GetName(), metav1.GetOptions{}) - } - if c.Create && errors.IsNotFound(err) { - log.Info(" Creating non-existent ", desc, dryRunText) - if !c.DryRun { - newobj, err = rc.Create(obj) - log.Debugf("Create(%s) returned (%v, %v)", obj.GetName(), newobj, err) - } else { - newobj = obj - err = nil - } - } - if err != nil { - // TODO: retry - return fmt.Errorf("Error updating %s: %s", desc, err) - } - - log.Debug("Updated object: ", kdiff.ObjectDiff(obj, newobj)) - - // Some objects appear under multiple kinds - // (eg: Deployment is both extensions/v1beta1 - // and apps/v1beta1). UID is the only stable - // identifier that links these two views of - // the same object. - seenUids.Insert(string(newobj.GetUID())) - } - - if c.GcTag != "" && !c.SkipGc { - version, err := utils.FetchVersion(discovery) - if err != nil { - return err - } - - err = walkObjects(clientPool, discovery, metav1.ListOptions{}, func(o runtime.Object) error { - meta, err := meta.Accessor(o) - if err != nil { - return err - } - gvk := o.GetObjectKind().GroupVersionKind() - desc := fmt.Sprintf("%s %s (%s)", utils.ResourceNameFor(discovery, o), utils.FqName(meta), gvk.GroupVersion()) - log.Debugf("Considering %v for gc", desc) - if eligibleForGc(meta, c.GcTag) && !seenUids.Has(string(meta.GetUID())) { - log.Info("Garbage collecting ", desc, dryRunText) - if !c.DryRun { - err := gcDelete(clientPool, discovery, &version, o) - if err != nil { - return err - } - } - } - return nil - }) - if err != nil { - return err - } - } - - return nil -} - -func stringListContains(list []string, value string) bool { - for _, item := range list { - if item == value { - return true - } - } - return false -} - -func gcDelete(clientpool dynamic.ClientPool, disco discovery.DiscoveryInterface, version *utils.ServerVersion, o runtime.Object) error { - obj, err := meta.Accessor(o) - if err != nil { - return fmt.Errorf("Unexpected object type: %s", err) - } - - uid := obj.GetUID() - desc := fmt.Sprintf("%s %s", utils.ResourceNameFor(disco, o), utils.FqName(obj)) - - deleteOpts := metav1.DeleteOptions{ - Preconditions: &metav1.Preconditions{UID: &uid}, - } - if version.Compare(1, 6) < 0 { - // 1.5.x option - boolFalse := false - deleteOpts.OrphanDependents = &boolFalse - } else { - // 1.6.x option (NB: Background is broken) - fg := metav1.DeletePropagationForeground - deleteOpts.PropagationPolicy = &fg - } - - c, err := utils.ClientForResource(clientpool, disco, o, metav1.NamespaceNone) - if err != nil { - return err - } - - err = c.Delete(obj.GetName(), &deleteOpts) - if err != nil && (errors.IsNotFound(err) || errors.IsConflict(err)) { - // We lost a race with something else changing the object - log.Debugf("Ignoring error while deleting %s: %s", desc, err) - err = nil - } - if err != nil { - return fmt.Errorf("Error deleting %s: %s", desc, err) - } - - return nil -} - -func walkObjects(pool dynamic.ClientPool, disco discovery.DiscoveryInterface, listopts metav1.ListOptions, callback func(runtime.Object) error) error { - rsrclists, err := disco.ServerResources() - if err != nil { - return err - } - for _, rsrclist := range rsrclists { - gv, err := schema.ParseGroupVersion(rsrclist.GroupVersion) - if err != nil { - return err - } - for _, rsrc := range rsrclist.APIResources { - gvk := gv.WithKind(rsrc.Kind) - - if !stringListContains(rsrc.Verbs, "list") { - log.Debugf("Don't know how to list %v, skipping", rsrc) - continue - } - client, err := pool.ClientForGroupVersionKind(gvk) - if err != nil { - return err - } - - var ns string - if rsrc.Namespaced { - ns = metav1.NamespaceAll - } else { - ns = metav1.NamespaceNone - } - - rc := client.Resource(&rsrc, ns) - log.Debugf("Listing %s", gvk) - obj, err := rc.List(listopts) - if err != nil { - return err - } - if err = meta.EachListItem(obj, callback); err != nil { - return err - } - } - } - return nil -} - -func eligibleForGc(obj metav1.Object, gcTag string) bool { - for _, ref := range obj.GetOwnerReferences() { - if ref.Controller != nil && *ref.Controller { - // Has a controller ref - return false - } - } - - a := obj.GetAnnotations() - - strategy, ok := a[AnnotationGcStrategy] - if !ok { - strategy = GcStrategyAuto - } - - return a[AnnotationGcTag] == gcTag && - strategy == GcStrategyAuto -} diff --git a/pkg/kubecfg/init.go b/pkg/kubecfg/init.go deleted file mode 100644 index efe08c56216d5be3632c2ad8aa66a83e6c0f5a03..0000000000000000000000000000000000000000 --- a/pkg/kubecfg/init.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2018 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 kubecfg - -import ( - "github.com/ksonnet/ksonnet/metadata" - log "github.com/sirupsen/logrus" -) - -type InitCmd struct { - name string - rootPath string - k8sSpecFlag *string - serverURI *string - namespace *string -} - -func NewInitCmd(name, rootPath string, k8sSpecFlag, serverURI, namespace *string) (*InitCmd, error) { - return &InitCmd{name: name, rootPath: rootPath, k8sSpecFlag: k8sSpecFlag, serverURI: serverURI, namespace: namespace}, nil -} - -func (c *InitCmd) Run() error { - _, err := metadata.Init(c.name, c.rootPath, c.k8sSpecFlag, c.serverURI, c.namespace) - if err == nil { - log.Info("ksonnet app successfully created! Next, try creating a component with `ks generate`.") - } - return err -} diff --git a/pkg/kubecfg/param.go b/pkg/kubecfg/param.go deleted file mode 100644 index bccd0bbdb97987603edc441b44f72a3502884d72..0000000000000000000000000000000000000000 --- a/pkg/kubecfg/param.go +++ /dev/null @@ -1,378 +0,0 @@ -// 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 kubecfg - -import ( - "fmt" - "io" - "os" - "reflect" - "sort" - "strconv" - - "github.com/fatih/color" - "github.com/ksonnet/ksonnet/component" - param "github.com/ksonnet/ksonnet/metadata/params" - "github.com/ksonnet/ksonnet/pkg/util/table" - str "github.com/ksonnet/ksonnet/strings" - "github.com/pkg/errors" - "github.com/spf13/afero" - - log "github.com/sirupsen/logrus" -) - -func sortedKeys(params map[string]param.Params) []string { - // keys maintains an alphabetically sorted list of the components - keys := make([]string, 0, len(params)) - for key := range params { - keys = append(keys, key) - } - sort.Strings(keys) - return keys -} - -func sortedParams(params param.Params) []string { - // keys maintains an alphabetically sorted list of the params - keys := make([]string, 0, len(params)) - for key := range params { - keys = append(keys, key) - } - sort.Strings(keys) - return keys -} - -// ---------------------------------------------------------------------------- - -// ParamSetCmd stores the information necessary to set component and -// environment params. -type ParamSetCmd struct { - component string - env string - - param string - value string -} - -// NewParamSetCmd acts as a constructor for ParamSetCmd. It will also sanitize -// or "quote" the param value first if necessary. -func NewParamSetCmd(component, env, param, value string) *ParamSetCmd { - return &ParamSetCmd{component: component, env: env, param: param, value: sanitizeParamValue(value)} -} - -// Run executes the setting of params. -func (c *ParamSetCmd) Run() error { - manager, err := manager() - if err != nil { - return err - } - - if len(c.env) == 0 { - if err = manager.SetComponentParams(c.component, param.Params{c.param: c.value}); err == nil { - log.Infof("Parameter '%s' successfully set to '%s' for component '%s'", c.param, c.value, c.component) - } - } else { - if err = manager.SetEnvironmentParams(c.env, c.component, param.Params{c.param: c.value}); err == nil { - log.Infof("Parameter '%s' successfully set to '%s' for component '%s' in environment '%s'", - c.param, c.value, c.component, c.env) - } - } - - return err -} - -// sanitizeParamValue does a best effort to identify value types. It will put -// quotes around values which it categorizes as strings. -func sanitizeParamValue(value string) string { - // numeric - if _, err := strconv.ParseFloat(value, 64); err == nil { - return value - } - // string - return fmt.Sprintf(`"%s"`, value) -} - -// ---------------------------------------------------------------------------- - -const ( - paramComponentHeader = "COMPONENT" - paramNameHeader = "PARAM" - paramValueHeader = "VALUE" -) - -// ParamListCmd stores the information necessary display component or -// environment parameters -type ParamListCmd struct { - fs afero.Fs - root string - component string - env string - nsName string -} - -// NewParamListCmd acts as a constructor for ParamListCmd. -func NewParamListCmd(component, env, nsName string) *ParamListCmd { - return &ParamListCmd{ - component: component, - env: env, - nsName: nsName, - } -} - -// Run executes the displaying of params. -func (c *ParamListCmd) Run(out io.Writer) error { - cwd, err := os.Getwd() - if err != nil { - return errors.Wrap(err, "get current working directory") - } - - manager, err := manager() - if err != nil { - return err - } - - var params map[string]param.Params - if len(c.env) != 0 { - params, err = manager.GetEnvironmentParams(c.env, c.nsName) - if err != nil { - return err - } - } else { - params, err = manager.GetAllComponentParams(cwd) - if err != nil { - return err - } - } - - if len(c.component) != 0 { - if _, ok := params[c.component]; !ok { - return fmt.Errorf("No such component '%s' found", c.component) - } - - p := params[c.component] - outputParamsFor(c.component, p, out) - return nil - } - - outputParams(params, out) - return nil -} - -func outputParamsFor(component string, params param.Params, out io.Writer) { - keys := sortedParams(params) - - t := table.New(out) - t.SetHeader([]string{paramNameHeader, paramValueHeader}) - for _, k := range keys { - t.Append([]string{k, params[k]}) - } - - t.Render() -} - -func outputParams(params map[string]param.Params, out io.Writer) { - keys := sortedKeys(params) - - t := table.New(out) - t.SetHeader([]string{paramComponentHeader, paramNameHeader, paramValueHeader}) - - for _, k := range keys { - // sort params to display alphabetically - ps := sortedParams(params[k]) - - for _, p := range ps { - t.Append([]string{k, p, params[k][p]}) - } - } - - t.Render() -} - -// ---------------------------------------------------------------------------- - -// ParamDiffCmd stores the information necessary to diff between environment -// parameters. -type ParamDiffCmd struct { - fs afero.Fs - root string - env1 string - env2 string - - component string -} - -// NewParamDiffCmd acts as a constructor for ParamDiffCmd. -func NewParamDiffCmd(fs afero.Fs, root, env1, env2, componentName string) *ParamDiffCmd { - return &ParamDiffCmd{ - fs: fs, - root: root, - env1: env1, - env2: env2, - component: componentName, - } -} - -type paramDiffRecord struct { - component string - param string - value1 string - value2 string -} - -// Run executes the diffing of environment params. -func (c *ParamDiffCmd) Run(out io.Writer) error { - const ( - componentHeader = "COMPONENT" - paramHeader = "PARAM" - ) - - manager, err := manager() - if err != nil { - return err - } - - ksApp, err := manager.App() - if err != nil { - return err - } - - ns, componentName := component.ExtractNamespacedComponent(ksApp, c.component) - - params1, err := manager.GetEnvironmentParams(c.env1, ns.Name()) - if err != nil { - return err - } - - params2, err := manager.GetEnvironmentParams(c.env2, ns.Name()) - if err != nil { - return err - } - - if len(c.component) != 0 { - params1 = map[string]param.Params{componentName: params1[componentName]} - params2 = map[string]param.Params{componentName: params2[componentName]} - } - - if reflect.DeepEqual(params1, params2) { - log.Info("No differences found.") - return nil - } - - componentNames := collectComponents(params1, params2) - - headers := str.Row{ - Content: []string{componentHeader, paramHeader, c.env1, c.env2}, - } - - var body []str.Row - for _, componentName := range componentNames { - paramNames := collectParams(params1[componentName], params2[componentName]) - - for _, paramName := range paramNames { - var v1, v2 string - - if p, ok := params1[componentName]; ok { - v1 = p[paramName] - } - - if p, ok := params2[componentName]; ok { - v2 = p[paramName] - } - - var bgColor *color.Color - if v1 == "" { - bgColor = color.New(color.BgGreen) - } else if v2 == "" { - bgColor = color.New(color.BgRed) - } else if v1 != v2 { - bgColor = color.New(color.BgYellow) - } - - body = append(body, str.Row{ - Content: []string{ - componentName, - paramName, - v1, - v2, - }, - Color: bgColor, - }) - } - } - - formatted, err := str.Table(headers, body) - if err != nil { - return err - } - - for _, row := range formatted { - if row.Color != nil { - _, err = row.Color.Fprint(out, row.Content) - if err != nil { - return err - } - // Must print new line separately otherwise color alignment will be - // incorrect. - fmt.Println() - } else { - _, err = fmt.Fprintln(out, row.Content) - if err != nil { - return err - } - } - } - - return nil -} - -func collectComponents(param1, param2 map[string]param.Params) []string { - m := make(map[string]bool) - for k := range param1 { - m[k] = true - } - for k := range param2 { - m[k] = true - } - - var names []string - - for k := range m { - names = append(names, k) - } - - sort.Strings(names) - - return names -} - -func collectParams(param1, param2 param.Params) []string { - m := make(map[string]bool) - for k := range param1 { - m[k] = true - } - for k := range param2 { - m[k] = true - } - - var names []string - - for k := range m { - names = append(names, k) - } - - sort.Strings(names) - - return names -} diff --git a/pkg/kubecfg/param_diff.go b/pkg/kubecfg/param_diff.go new file mode 100644 index 0000000000000000000000000000000000000000..f302467188879ce5247a630e6de56afe229d4987 --- /dev/null +++ b/pkg/kubecfg/param_diff.go @@ -0,0 +1,205 @@ +// 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 kubecfg + +import ( + "fmt" + "io" + "reflect" + "sort" + + "github.com/fatih/color" + "github.com/ksonnet/ksonnet/component" + param "github.com/ksonnet/ksonnet/metadata/params" + str "github.com/ksonnet/ksonnet/strings" + log "github.com/sirupsen/logrus" + "github.com/spf13/afero" +) + +// ParamDiffCmd stores the information necessary to diff between environment +// parameters. +type ParamDiffCmd struct { + fs afero.Fs + root string + env1 string + env2 string + + component string +} + +// NewParamDiffCmd acts as a constructor for ParamDiffCmd. +func NewParamDiffCmd(fs afero.Fs, root, env1, env2, componentName string) *ParamDiffCmd { + return &ParamDiffCmd{ + fs: fs, + root: root, + env1: env1, + env2: env2, + component: componentName, + } +} + +type paramDiffRecord struct { + component string + param string + value1 string + value2 string +} + +// Run executes the diffing of environment params. +func (c *ParamDiffCmd) Run(out io.Writer) error { + const ( + componentHeader = "COMPONENT" + paramHeader = "PARAM" + ) + + manager, err := manager() + if err != nil { + return err + } + + ksApp, err := manager.App() + if err != nil { + return err + } + + ns, componentName := component.ExtractNamespacedComponent(ksApp, c.component) + + params1, err := manager.GetEnvironmentParams(c.env1, ns.Name()) + if err != nil { + return err + } + + params2, err := manager.GetEnvironmentParams(c.env2, ns.Name()) + if err != nil { + return err + } + + if len(c.component) != 0 { + params1 = map[string]param.Params{componentName: params1[componentName]} + params2 = map[string]param.Params{componentName: params2[componentName]} + } + + if reflect.DeepEqual(params1, params2) { + log.Info("No differences found.") + return nil + } + + componentNames := collectComponents(params1, params2) + + headers := str.Row{ + Content: []string{componentHeader, paramHeader, c.env1, c.env2}, + } + + var body []str.Row + for _, componentName := range componentNames { + paramNames := collectParams(params1[componentName], params2[componentName]) + + for _, paramName := range paramNames { + var v1, v2 string + + if p, ok := params1[componentName]; ok { + v1 = p[paramName] + } + + if p, ok := params2[componentName]; ok { + v2 = p[paramName] + } + + var bgColor *color.Color + if v1 == "" { + bgColor = color.New(color.BgGreen) + } else if v2 == "" { + bgColor = color.New(color.BgRed) + } else if v1 != v2 { + bgColor = color.New(color.BgYellow) + } + + body = append(body, str.Row{ + Content: []string{ + componentName, + paramName, + v1, + v2, + }, + Color: bgColor, + }) + } + } + + formatted, err := str.Table(headers, body) + if err != nil { + return err + } + + for _, row := range formatted { + if row.Color != nil { + _, err = row.Color.Fprint(out, row.Content) + if err != nil { + return err + } + // Must print new line separately otherwise color alignment will be + // incorrect. + fmt.Println() + } else { + _, err = fmt.Fprintln(out, row.Content) + if err != nil { + return err + } + } + } + + return nil +} + +func collectComponents(param1, param2 map[string]param.Params) []string { + m := make(map[string]bool) + for k := range param1 { + m[k] = true + } + for k := range param2 { + m[k] = true + } + + var names []string + + for k := range m { + names = append(names, k) + } + + sort.Strings(names) + + return names +} + +func collectParams(param1, param2 param.Params) []string { + m := make(map[string]bool) + for k := range param1 { + m[k] = true + } + for k := range param2 { + m[k] = true + } + + var names []string + + for k := range m { + names = append(names, k) + } + + sort.Strings(names) + + return names +} diff --git a/pkg/kubecfg/param_test.go b/pkg/kubecfg/param_test.go deleted file mode 100644 index 797cbabab3a8ef0dac207fbdc93f8a6629483ec4..0000000000000000000000000000000000000000 --- a/pkg/kubecfg/param_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// 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 kubecfg - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestSanitizeParamValue(t *testing.T) { - tests := []struct { - value string - expected string - }{ - // numbers - { - value: "64.5", - expected: "64.5", - }, - { - value: "64", - expected: "64", - }, - // boolean - { - value: "false", - expected: `"false"`, - }, - // string - { - value: "my string", - expected: `"my string"`, - }, - } - - for _, te := range tests { - got := sanitizeParamValue(te.value) - require.Equal(t, got, te.expected, "Unexpected sanitized param value") - } -} diff --git a/pkg/registry/github.go b/pkg/registry/github.go index d8d7a1dc65ae18da85acbfeed73ee750a708d03d..c9bf37fc5e1416c7c31d190929e74a2dccb547b8 100644 --- a/pkg/registry/github.go +++ b/pkg/registry/github.go @@ -31,10 +31,6 @@ import ( const ( rawGitHubRoot = "https://raw.githubusercontent.com" defaultGitHubBranch = "master" - - uriField = "uri" - refSpecField = "refSpec" - resolvedSHAField = "resolvedSHA" ) var ( diff --git a/pkg/util/github/github.go b/pkg/util/github/github.go index 3724d5f78a8d4c555540809b329fe5fa69920584..9f11ae682de74f0df54661bc45b9c1d5e0ddf8d0 100644 --- a/pkg/util/github/github.go +++ b/pkg/util/github/github.go @@ -17,7 +17,6 @@ package github import ( "context" - "fmt" "net/http" "os" @@ -26,16 +25,9 @@ import ( "golang.org/x/oauth2" ) -const ( - registryYAMLFile = "registry.yaml" - defaultGitHubBranch = "master" -) - var ( // DefaultClient is the default GitHub client. DefaultClient = &defaultGitHub{} - - errInvalidURI = fmt.Errorf("Invalid GitHub URI: try navigating in GitHub to the URI of the folder containing the 'yaml', and using that URI instead. Generally, this URI should be of the form 'github.com/{organization}/{repository}/tree/{branch}/[path-to-directory]'") ) // Repo is a GitHub repo diff --git a/pkg/util/test/assert.go b/pkg/util/test/assert.go new file mode 100644 index 0000000000000000000000000000000000000000..079752fbe9c3f3d7a2092db4f3a5983c3ad27440 --- /dev/null +++ b/pkg/util/test/assert.go @@ -0,0 +1,53 @@ +// 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 test + +import ( + "io/ioutil" + "path/filepath" + "testing" + + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// AssertExists asserts if a path exists. +func AssertExists(t *testing.T, fs afero.Fs, path string) { + exists, err := afero.Exists(fs, path) + require.NoError(t, err) + + assert.True(t, exists, "%q does not exist", path) +} + +// AssertNotExists asserts a path does exist. +func AssertNotExists(t *testing.T, fs afero.Fs, path string) { + exists, err := afero.Exists(fs, path) + require.NoError(t, err) + + assert.False(t, exists, "%q exists", path) +} + +// AssertContents asserts the contents of a file. +func AssertContents(t *testing.T, fs afero.Fs, expectedPath, contentPath string) { + expected, err := ioutil.ReadFile(filepath.Join("testdata", expectedPath)) + require.NoError(t, err) + + got, err := afero.ReadFile(fs, contentPath) + require.NoError(t, err) + + assert.Equal(t, string(expected), string(got), "unexpected %q contents", contentPath) +}