forked from kyma-project/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
1,116 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package apply | ||
|
||
import ( | ||
"github.com/kyma-project/cli/cmd/kyma/apply/function" | ||
"github.com/kyma-project/cli/internal/cli" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
//NewCmd creates a new function command | ||
func NewCmd(o *cli.Options) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "apply", | ||
Short: "Applies local resources to the Kyma cluster.", | ||
Long: "Use this command to apply the resource configuration to the Kyma cluster.", | ||
} | ||
|
||
cmd.AddCommand(function.NewCmd(function.NewOptions(o))) | ||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package apply | ||
|
||
import ( | ||
"github.com/kyma-project/cli/internal/cli" | ||
"github.com/stretchr/testify/require" | ||
"io/ioutil" | ||
"testing" | ||
) | ||
|
||
func TestSubcommands(t *testing.T) { | ||
c := NewCmd(&cli.Options{}) | ||
c.SetOutput(ioutil.Discard) // not interested in the command's output | ||
|
||
// test default flag values | ||
require.NoError(t, c.Execute(), "Command execution must not fail") | ||
|
||
sub := c.Commands() | ||
|
||
require.Equal(t, 2, len(sub), "Number of created subcommands not as expected") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
package function | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"github.com/kyma-incubator/hydroform/function/pkg/client" | ||
"github.com/kyma-incubator/hydroform/function/pkg/manager" | ||
"github.com/kyma-incubator/hydroform/function/pkg/operator" | ||
resources "github.com/kyma-incubator/hydroform/function/pkg/resources/unstructured" | ||
"github.com/kyma-incubator/hydroform/function/pkg/workspace" | ||
"github.com/kyma-project/cli/internal/cli" | ||
"github.com/kyma-project/cli/internal/kube" | ||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
"gopkg.in/yaml.v2" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"os" | ||
) | ||
|
||
type command struct { | ||
opts *Options | ||
cli.Command | ||
} | ||
|
||
//NewCmd creates a new apply command | ||
func NewCmd(o *Options) *cobra.Command { | ||
c := command{ | ||
opts: o, | ||
Command: cli.Command{Options: o.Options}, | ||
} | ||
cmd := &cobra.Command{ | ||
Use: "function", | ||
Short: "Applies local resources for your Function to the Kyma cluster.", | ||
Long: `Use this command to apply the local sources of your Function's code and dependencies to the Kyma cluster. | ||
Use the flags to specify the desired location for the source files or run the command to validate and print the output resources.`, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return c.Run() | ||
}, | ||
} | ||
|
||
cmd.Flags().StringVarP(&o.Filename, "filename", "f", "", `Full path to the config file.`) | ||
cmd.Flags().BoolVar(&o.DryRun, "dry-run", false, `Validated list of objects to be created from sources.`) | ||
cmd.Flags().Var(&o.OnError, "onerror", `Flag used to define the Kyma CLI's reaction to an error when applying resources to the cluster. Use one of these options: | ||
- nothing | ||
- purge`) | ||
cmd.Flags().VarP(&o.Output, "output", "o", `Flag used to define the command output format. Use one of these options: | ||
- text | ||
- json | ||
- yaml | ||
- none`) | ||
|
||
return cmd | ||
} | ||
|
||
func (c *command) Run() error { | ||
if c.opts.Filename == "" { | ||
c.opts.Filename = defaultFilename() | ||
} | ||
|
||
file, err := os.Open(c.opts.Filename) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Load project configuration | ||
var configuration workspace.Cfg | ||
if err := yaml.NewDecoder(file).Decode(&configuration); err != nil { | ||
return errors.Wrap(err, "Could not decode the configuration file") | ||
} | ||
|
||
if c.K8s, err = kube.NewFromConfig("", c.KubeconfigPath); err != nil { | ||
return errors.Wrap(err, "Could not initialize the Kubernetes client. Make sure your kubeconfig is valid") | ||
} | ||
client := c.K8s.Dynamic() | ||
|
||
function, err := resources.NewFunction(configuration) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
triggers, err := resources.NewTriggers(configuration) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
operators := map[operator.Operator][]operator.Operator{ | ||
operator.NewGenericOperator(client.Resource(operator.GVKFunction).Namespace(configuration.Namespace), function): { | ||
operator.NewTriggersOperator(client.Resource(operator.GVKTriggers).Namespace(configuration.Namespace), triggers...), | ||
}, | ||
} | ||
|
||
if configuration.Source.Type == workspace.SourceTypeGit { | ||
gitRepository, err := resources.NewPublicGitRepository(configuration) | ||
if err != nil { | ||
return errors.Wrap(err, "Unable to read the Git repository from the provided configuration") | ||
} | ||
gitOperator := operator.NewGenericOperator(client.Resource(operator.GVRGitRepository).Namespace(configuration.Namespace), gitRepository) | ||
operators[gitOperator] = nil | ||
} | ||
|
||
mgr := manager.NewManager(operators) | ||
options := manager.Options{ | ||
Callbacks: callbacks(c), | ||
OnError: chooseOnError(c.opts.OnError), | ||
DryRun: c.opts.DryRun, | ||
SetOwnerReferences: true, | ||
} | ||
|
||
return mgr.Do(context.Background(), options) | ||
} | ||
|
||
const ( | ||
operatingFormat = "%s - %s operating... %s" | ||
createdFormat = "%s - %s created %s" | ||
updatedFormat = "%s - %s updated %s" | ||
skippedFormat = "%s - %s unchanged %s" | ||
deletedFormat = "%s - %s deleted %s" | ||
applyFailedFormat = "%s - %s can't be applied %s" | ||
deleteFailedFormat = "%s - %s can't be removed %s" | ||
unknownStatusFormat = "%s - %s can't resolve status %s" | ||
dryRunSuffix = "(dry run)" | ||
yamlFormat = "---\n%s\n" | ||
jsonFormat = "%s\n" | ||
) | ||
|
||
func chooseOnError(onErr value) manager.OnError { | ||
if onErr.value == NothingOnError { | ||
return manager.NothingOnError | ||
|
||
} | ||
return manager.PurgeOnError | ||
} | ||
|
||
func (l *logger) chooseFormat(status client.StatusType) string { | ||
switch status { | ||
case client.StatusTypeCreated: | ||
return createdFormat | ||
case client.StatusTypeUpdated: | ||
return updatedFormat | ||
case client.StatusTypeSkipped: | ||
return skippedFormat | ||
case client.StatusTypeDeleted: | ||
return deletedFormat | ||
case client.StatusTypeApplyFailed: | ||
return applyFailedFormat | ||
case client.StatusTypeDeleteFailed: | ||
return deleteFailedFormat | ||
|
||
} | ||
return unknownStatusFormat | ||
} | ||
|
||
func (l *logger) formatSuffix() string { | ||
if l.opts.DryRun { | ||
return dryRunSuffix | ||
} | ||
return "" | ||
} | ||
|
||
type logger struct { | ||
*command | ||
} | ||
|
||
func callbacks(c *command) operator.Callbacks { | ||
logger := logger{c} | ||
return operator.Callbacks{ | ||
Pre: []operator.Callback{ | ||
logger.pre, | ||
}, | ||
Post: []operator.Callback{ | ||
logger.post, | ||
}, | ||
} | ||
} | ||
|
||
func (l *logger) pre(v interface{}, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
entry, ok := v.(*unstructured.Unstructured) | ||
if !ok { | ||
return errors.New("can't parse interface{} to the Unstructured") | ||
} | ||
|
||
switch l.opts.Output.String() { | ||
case TextOutput: | ||
info := fmt.Sprintf(operatingFormat, entry.GetKind(), entry.GetName(), l.formatSuffix()) | ||
l.NewStep(info) | ||
return nil | ||
case JSONOutput: | ||
if err != nil { | ||
return err | ||
} | ||
unstructured.RemoveNestedField(entry.Object, "metadata", "ownerReferences") | ||
unstructured.RemoveNestedField(entry.Object, "metadata", "labels", "ownerID") | ||
bytes, marshalError := json.MarshalIndent(entry.Object, "", " ") | ||
if marshalError != nil { | ||
return marshalError | ||
} | ||
fmt.Printf(jsonFormat, string(bytes)) | ||
return nil | ||
case YAMLOutput: | ||
if err != nil { | ||
return err | ||
} | ||
unstructured.RemoveNestedField(entry.Object, "metadata", "ownerReferences") | ||
unstructured.RemoveNestedField(entry.Object, "metadata", "labels", "ownerID") | ||
bytes, marshalError := yaml.Marshal(entry.Object) | ||
if marshalError != nil { | ||
return marshalError | ||
} | ||
fmt.Printf(yamlFormat, string(bytes)) | ||
return nil | ||
case NoneOutput: | ||
return err | ||
} | ||
return err | ||
} | ||
|
||
func (l *logger) post(v interface{}, err error) error { | ||
entry, ok := v.(client.PostStatusEntry) | ||
if !ok { | ||
return errors.New("can't parse interface{} to StatusEntry interface") | ||
} | ||
|
||
if l.opts.Output.String() != TextOutput { | ||
return err | ||
} | ||
format := l.chooseFormat(entry.StatusType) | ||
info := fmt.Sprintf(format, entry.GetKind(), entry.GetName(), l.formatSuffix()) | ||
step := l.CurrentStep | ||
if err != nil { | ||
step.Failuref(info) | ||
} | ||
step.Successf(info) | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package function | ||
|
||
import ( | ||
"github.com/kyma-project/cli/internal/cli" | ||
"github.com/stretchr/testify/require" | ||
"testing" | ||
) | ||
|
||
// TestUpgradeFlags ensures that the provided command flags are stored in the options. | ||
func TestUpgradeFlags(t *testing.T) { | ||
o := NewOptions(&cli.Options{}) | ||
c := NewCmd(o) | ||
|
||
// test default flag values | ||
require.Equal(t, false, o.DryRun, "Default value for the --dry-run flag not as expected.") | ||
require.Equal(t, "", o.Filename, "Default value for the --filename flag not as expected.") | ||
require.Equal(t, "nothing", o.OnError.String(), "The parsed value for the --onerror flag not as expected.") | ||
require.Equal(t, "text", o.Output.String(), "The parsed value for the --output flag not as expected.") | ||
|
||
// test passing flags | ||
err := c.ParseFlags([]string{ | ||
"--filename", "/fakepath/config.yaml", | ||
"--dry-run", "true", | ||
"--onerror", "purge", | ||
"--output", "json", | ||
}) | ||
require.NoError(t, err, "Parsing flags should not return an error") | ||
require.Equal(t, "/fakepath/config.yaml", o.Filename, "The parsed value for the --filename flag not as expected.") | ||
require.Equal(t, true, o.DryRun, "The parsed value for the --dry-run flag not as expected.") | ||
require.Equal(t, "purge", o.OnError.String(), "The parsed value for the --onerror flag not as expected.") | ||
require.Equal(t, "json", o.Output.String(), "The parsed value for the --output flag not as expected.") | ||
|
||
err = c.ParseFlags([]string{ | ||
"-f", "/config.yaml", | ||
"-o", "yaml", | ||
}) | ||
require.NoError(t, err, "Parsing flags should not return an error") | ||
require.Equal(t, "/config.yaml", o.Filename, "The parsed value for the -f flag not as expected.") | ||
require.Equal(t, "yaml", o.Output.String(), "The parsed value for the -o flag not as expected.") | ||
} |
Oops, something went wrong.