diff --git a/cmd/apply.go b/cmd/apply.go index 9d4221db2..df210c11b 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -43,7 +43,7 @@ var applyCmd = &cobra.Command{ kubectlCommand = append(kubectlCommand, "--namespace", Namespace) } - if err := pkgcmd.ExecuteKubectl(InputFiles, kubectlCommand...); err != nil { + if err := pkgcmd.CreateKubernetesArtifacts(InputFiles, false, kubectlCommand...); err != nil { fmt.Println(err) os.Exit(-1) } diff --git a/cmd/create.go b/cmd/create.go index 7004f6723..77406a9f1 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -42,7 +42,7 @@ var createCmd = &cobra.Command{ kubectlCommand = append(kubectlCommand, "--namespace", Namespace) } - if err := pkgcmd.ExecuteKubectl(InputFiles, kubectlCommand...); err != nil { + if err := pkgcmd.CreateKubernetesArtifacts(InputFiles, false, kubectlCommand...); err != nil { fmt.Println(err) os.Exit(-1) } diff --git a/cmd/delete.go b/cmd/delete.go index 84b71afc8..01c1d4b56 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -42,7 +42,7 @@ var deleteCmd = &cobra.Command{ kubectlCommand = append(kubectlCommand, "--namespace", Namespace) } - if err := pkgcmd.ExecuteKubectl(InputFiles, kubectlCommand...); err != nil { + if err := pkgcmd.CreateKubernetesArtifacts(InputFiles, false, kubectlCommand...); err != nil { fmt.Println(err) os.Exit(-1) } diff --git a/cmd/generate.go b/cmd/generate.go index 24603113c..ceff26c23 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -33,7 +33,7 @@ var generateCmd = &cobra.Command{ fmt.Println(err) os.Exit(-1) } - if err := pkgcmd.Generate(InputFiles); err != nil { + if err := pkgcmd.CreateKubernetesArtifacts(InputFiles, true, ""); err != nil { fmt.Println(err) os.Exit(-1) } diff --git a/cmd/root.go b/cmd/root.go index b703da358..5522972c2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -19,6 +19,7 @@ package cmd import ( "fmt" "os" + "strings" log "github.com/Sirupsen/logrus" "github.com/spf13/cobra" @@ -26,7 +27,8 @@ import ( // Global variables var ( - GlobalVerbose bool + GlobalVerbose bool + GlobalProvider string ) // RootCmd represents the base command when called without any subcommands @@ -40,6 +42,12 @@ var RootCmd = &cobra.Command{ log.SetLevel(log.DebugLevel) } + // Error out of the user has not chosen Kubernetes or OpenShift + provider := strings.ToLower(GlobalProvider) + if provider != "kubernetes" && provider != "openshift" { + log.Fatalf("%s is an unsupported provider. Supported providers are: 'kubernetes', 'openshift'.", GlobalProvider) + } + }, } @@ -53,4 +61,5 @@ func Execute() { // Initialize all flags func init() { RootCmd.PersistentFlags().BoolVarP(&GlobalVerbose, "verbose", "v", false, "verbose output") + RootCmd.PersistentFlags().StringVar(&GlobalProvider, "provider", "kubernetes", "Specify a provider. Kubernetes or OpenShift.") } diff --git a/pkg/cmd/generate.go b/pkg/cmd/generate.go deleted file mode 100644 index de3e76e1b..000000000 --- a/pkg/cmd/generate.go +++ /dev/null @@ -1,101 +0,0 @@ -/* -Copyright 2017 The Kedge Authors All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package cmd - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - - "github.com/kedgeproject/kedge/pkg/spec" - - "github.com/ghodss/yaml" - "github.com/pkg/errors" -) - -func Generate(paths []string) error { - - files, err := GetAllYAMLFiles(paths) - if err != nil { - return errors.Wrap(err, "unable to get YAML files") - } - - inputs, err := getApplicationsFromFiles(files) - if err != nil { - return errors.Wrap(err, "unable to get kedge definitions from input files") - } - - for _, input := range inputs { - - ros, extraResources, err := spec.CoreOperations(input.data) - if err != nil { - return errors.Wrap(err, "unable to perform controller operations") - } - - // write all the kubernetes objects that were generated - for _, runtimeObject := range ros { - - data, err := yaml.Marshal(runtimeObject) - if err != nil { - return errors.Wrap(err, "failed to marshal object") - } - - err = writeObject(data) - if err != nil { - return errors.Wrap(err, "failed to write object") - } - } - - for _, file := range extraResources { - // change the file name to absolute file name - // then read the file and then pass it to writeObject - file = findAbsPath(input.fileName, file) - data, err := ioutil.ReadFile(file) - if err != nil { - return errors.Wrap(err, "file reading failed") - } - err = writeObject(data) - if err != nil { - return errors.Wrap(err, "failed to write object") - } - } - } - return nil -} - -func writeObject(data []byte) error { - _, err := fmt.Fprintln(os.Stdout, "---") - if err != nil { - return errors.Wrap(err, "could not print to STDOUT") - } - - _, err = os.Stdout.Write(data) - return errors.Wrap(err, "could not write to STDOUT") -} - -func findAbsPath(baseFilePath, path string) string { - // TODO: if the baseFilePath is empty then just take the - // pwd as basefilePath, here we will force user to - // use the kedge binary from the directory that has files - // otherwise there is no way of knowing where the files will be - // this condition will happen when we add support for reading from the stdin - if filepath.IsAbs(path) { - return path - } - return filepath.Join(filepath.Dir(baseFilePath), path) -} diff --git a/pkg/cmd/kubernetes.go b/pkg/cmd/kubernetes.go index bc1e1dfe0..f3dfeb2c0 100644 --- a/pkg/cmd/kubernetes.go +++ b/pkg/cmd/kubernetes.go @@ -19,7 +19,10 @@ package cmd import ( "fmt" "io" + "io/ioutil" + "os" "os/exec" + "path/filepath" "github.com/kedgeproject/kedge/pkg/spec" @@ -27,7 +30,9 @@ import ( "github.com/pkg/errors" ) -func ExecuteKubectl(paths []string, args ...string) error { +// Generate Kubernetes Artifacts and either writes to file +// or uses kubectl to deploy. +func CreateKubernetesArtifacts(paths []string, generate bool, args ...string) error { files, err := GetAllYAMLFiles(paths) if err != nil { @@ -46,38 +51,61 @@ func ExecuteKubectl(paths []string, args ...string) error { return errors.Wrap(err, "unable to perform controller operations") } - for _, o := range ros { - data, err := yaml.Marshal(o) + for _, runtimeObject := range ros { + + // Unmarshal said object + data, err := yaml.Marshal(runtimeObject) if err != nil { return errors.Wrap(err, "failed to marshal object") } - // We need to add "-f -" at the end of the command passed to us to - // pass the generated files. - // e.g. If the command and arguments are "apply --namespace staging", then the - // final command becomes "kubectl apply --namespace staging -f -" - arguments := append(args, "-f", "-") - err = runKubectl(arguments, data) - if err != nil { - return errors.Wrap(err, "kubectl error") + + // Write to file if generate = true + if generate { + err = writeObject(data) + if err != nil { + return errors.Wrap(err, "failed to write object") + } + } else { + // We need to add "-f -" at the end of the command passed to us to + // pass the generated files. + // e.g. If the command and arguments are "apply --namespace staging", then the + // final command becomes "kubectl apply --namespace staging -f -" + arguments := append(args, "-f", "-") + err = runKubectl(arguments, data) + if err != nil { + return errors.Wrap(err, "kubectl error") + } } + } for _, file := range extraResources { // change the file name to absolute file name file = findAbsPath(input.fileName, file) - // We need to add "-f absolute-filename" at the end of the command passed to us to - // pass the generated files. - // e.g. If the command and arguments are "apply --namespace staging", then the - // final command becomes "kubectl apply --namespace staging -f absolute-filename" - arguments := append(args, "-f", file) - err = runKubectl(arguments, nil) - if err != nil { - return errors.Wrap(err, "kubectl error") + if generate { + data, err := ioutil.ReadFile(file) + if err != nil { + return errors.Wrap(err, "file reading failed") + } + err = writeObject(data) + if err != nil { + return errors.Wrap(err, "failed to write object") + } + } else { + + // We need to add "-f absolute-filename" at the end of the command passed to us to + // pass the generated files. + // e.g. If the command and arguments are "apply --namespace staging", then the + // final command becomes "kubectl apply --namespace staging -f absolute-filename" + arguments := append(args, "-f", file) + err = runKubectl(arguments, nil) + if err != nil { + return errors.Wrap(err, "kubectl error") + } } } } - return nil } @@ -105,3 +133,25 @@ func runKubectl(args []string, data []byte) error { fmt.Printf("%s", string(out)) return nil } + +func writeObject(data []byte) error { + _, err := fmt.Fprintln(os.Stdout, "---") + if err != nil { + return errors.Wrap(err, "could not print to STDOUT") + } + + _, err = os.Stdout.Write(data) + return errors.Wrap(err, "could not write to STDOUT") +} + +func findAbsPath(baseFilePath, path string) string { + // TODO: if the baseFilePath is empty then just take the + // pwd as basefilePath, here we will force user to + // use the kedge binary from the directory that has files + // otherwise there is no way of knowing where the files will be + // this condition will happen when we add support for reading from the stdin + if filepath.IsAbs(path) { + return path + } + return filepath.Join(filepath.Dir(baseFilePath), path) +} diff --git a/pkg/cmd/kubernetes_test.go b/pkg/cmd/kubernetes_test.go new file mode 100644 index 000000000..6ecc838a7 --- /dev/null +++ b/pkg/cmd/kubernetes_test.go @@ -0,0 +1,46 @@ +/* +Copyright 2017 The Kedge Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "io/ioutil" + "os" + "testing" +) + +func TestWriteObject(t *testing.T) { + + err := writeObject([]byte("foobar")) + if err != nil { + t.Fatalf("Error FindAbsPath: %s", err) + } + +} + +func TestFindAbsPath(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "absPathTest") + if err != nil { + t.Fatal("creating temp dir:", err) + } + defer os.RemoveAll(tmpDir) + + path := findAbsPath("", tmpDir) + + if path != tmpDir { + t.Fatal("Error getting correct absolute path") + } +}