diff --git a/flytectl/.github.com/PULL_REQUEST_TEMPLATE.md b/flytectl/.github.com/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..d2becf38b7 --- /dev/null +++ b/flytectl/.github.com/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,26 @@ +# TL;DR +_Please replace this text with a description of what this PR accomplishes._ + +## Type + - [ ] Bug Fix + - [ ] Feature + - [ ] Plugin + +## Are all requirements met? + + - [ ] Code completed + - [ ] Smoke tested + - [ ] Unit tests added + - [ ] Code documentation added + - [ ] Any pending items have an associated Issue + +## Complete description + _How did you fix the bug, make the feature etc. Link to any design docs etc_ + +## Tracking Issue +https://github.com/lyft/flyte/issues/ + +## Follow-up issue +_NA_ +OR +_https://github.com/lyft/flyte/issues/_ diff --git a/flytectl/.github.com/workflow/pull_request.yaml b/flytectl/.github.com/workflow/pull_request.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/flytectl/README.md b/flytectl/README.md index dfe4413421..28f29f2a50 100644 --- a/flytectl/README.md +++ b/flytectl/README.md @@ -1,5 +1,9 @@ # flytectl -Flyte CLI +Install Flyte CLI +```bash +curl -s https://github.com/lyft/flytectl/blob/master/install.sh | bash +``` [Contribution guidelines for this project](docs/CONTRIBUTING.md) + diff --git a/flytectl/cmd/config/config.go b/flytectl/cmd/config/config.go index 406279ec71..bb691b52c0 100644 --- a/flytectl/cmd/config/config.go +++ b/flytectl/cmd/config/config.go @@ -6,7 +6,7 @@ import ( "github.com/lyft/flytestdlib/config" - "github.com/lyft/flytectl/printer" + "github.com/lyft/flytectl/pkg/printer" ) //go:generate pflags Config @@ -16,16 +16,19 @@ var ( section = config.MustRegisterSection("root", defaultConfig) ) +// Config hold configration for flytectl flag type Config struct { Project string `json:"project" pflag:",Specifies the project to work on."` Domain string `json:"domain" pflag:",Specified the domain to work on."` Output string `json:"output" pflag:",Specified the output type."` } +// OutputFormat will return output formate func (cfg Config) OutputFormat() (printer.OutputFormat, error) { return printer.OutputFormatString(strings.ToUpper(cfg.Output)) } +// MustOutputFormat will validate the supported output formate and return output formate func (cfg Config) MustOutputFormat() printer.OutputFormat { f, err := cfg.OutputFormat() if err != nil { @@ -34,6 +37,7 @@ func (cfg Config) MustOutputFormat() printer.OutputFormat { return f } +// GetConfig will return the config func GetConfig() *Config { return section.GetConfig().(*Config) } diff --git a/flytectl/cmd/core/cmd.go b/flytectl/cmd/core/cmd.go index 96e62950ba..07d57bb14c 100644 --- a/flytectl/cmd/core/cmd.go +++ b/flytectl/cmd/core/cmd.go @@ -13,14 +13,16 @@ import ( type CommandEntry struct { ProjectDomainNotRequired bool CmdFunc CommandFunc + Aliases []string } func AddCommands(rootCmd *cobra.Command, cmdFuncs map[string]CommandEntry) { for resource, cmdEntry := range cmdFuncs { cmd := &cobra.Command{ - Use: resource, - Short: fmt.Sprintf("Retrieves %v resources.", resource), - RunE: generateCommandFunc(cmdEntry), + Use: resource, + Short: fmt.Sprintf("Retrieves %v resources.", resource), + Aliases: cmdEntry.Aliases, + RunE: generateCommandFunc(cmdEntry), } rootCmd.AddCommand(cmd) diff --git a/flytectl/cmd/get/get.go b/flytectl/cmd/get/get.go index 19e80a4a64..097206c2ff 100644 --- a/flytectl/cmd/get/get.go +++ b/flytectl/cmd/get/get.go @@ -6,6 +6,7 @@ import ( "github.com/spf13/cobra" ) +// CreateGetCommand will return get command func CreateGetCommand() *cobra.Command { getCmd := &cobra.Command{ Use: "get", @@ -13,9 +14,10 @@ func CreateGetCommand() *cobra.Command { } getResourcesFuncs := map[string]cmdcore.CommandEntry{ - "projects": {CmdFunc: getProjectsFunc, ProjectDomainNotRequired: true}, - "tasks": {CmdFunc: getTaskFunc}, - "workflows": {CmdFunc: getWorkflowFunc}, + "project": {CmdFunc: getProjectsFunc, Aliases: []string{"projects"}, ProjectDomainNotRequired: true}, + "task": {CmdFunc: getTaskFunc, Aliases: []string{"tasks"}}, + "workflow": {CmdFunc: getWorkflowFunc, Aliases: []string{"workflows"}}, + "launchplan": {CmdFunc: getLaunchPlanFunc, Aliases: []string{"launchplans"}}, } cmdcore.AddCommands(getCmd, getResourcesFuncs) diff --git a/flytectl/cmd/get/launch_plan.go b/flytectl/cmd/get/launch_plan.go new file mode 100644 index 0000000000..0f31e5106b --- /dev/null +++ b/flytectl/cmd/get/launch_plan.go @@ -0,0 +1,61 @@ +package get + +import ( + "context" + "github.com/golang/protobuf/proto" + "github.com/lyft/flytectl/cmd/config" + cmdCore "github.com/lyft/flytectl/cmd/core" + "github.com/lyft/flytectl/pkg/adminutils" + "github.com/lyft/flytectl/pkg/printer" + "github.com/lyft/flyteidl/gen/pb-go/flyteidl/admin" + "github.com/lyft/flytestdlib/logger" +) + +var launchplanColumns = []printer.Column{ + {"Version", "$.id.version"}, + {"Name", "$.id.name"}, + {"Type", "$.closure.compiledTask.template.type"}, + {"State", "$.spec.state"}, + {"Schedule", "$.spec.entityMetadata.schedule"}, +} + +func LaunchplanToProtoMessages(l []*admin.LaunchPlan) []proto.Message { + messages := make([]proto.Message, 0, len(l)) + for _, m := range l { + messages = append(messages, m) + } + return messages +} + +func getLaunchPlanFunc(ctx context.Context, args []string, cmdCtx cmdCore.CommandContext) error { + launchPlanPrinter := printer.Printer{} + + if len(args) == 1 { + name := args[0] + launchPlan, err := cmdCtx.AdminClient().ListLaunchPlans(ctx, &admin.ResourceListRequest{ + Limit: 10, + Id: &admin.NamedEntityIdentifier{ + Project: config.GetConfig().Project, + Domain: config.GetConfig().Domain, + Name: name, + }, + }) + if err != nil { + return err + } + logger.Debugf(ctx, "Retrieved %v excutions", len(launchPlan.LaunchPlans)) + err = launchPlanPrinter.Print(config.GetConfig().MustOutputFormat(), launchplanColumns, LaunchplanToProtoMessages(launchPlan.LaunchPlans)...) + if err != nil { + return err + } + return nil + } + + launchPlans, err := adminutils.GetAllNamedEntities(ctx, cmdCtx.AdminClient().ListLaunchPlanIds, adminutils.ListRequest{Project: config.GetConfig().Project, Domain: config.GetConfig().Domain}) + if err != nil { + return err + } + logger.Debugf(ctx, "Retrieved %v launch plans", len(launchPlans)) + return launchPlanPrinter.Print(config.GetConfig().MustOutputFormat(), entityColumns, adminutils.NamedEntityToProtoMessage(launchPlans)...) + return nil +} diff --git a/flytectl/cmd/get/named_entity.go b/flytectl/cmd/get/named_entity.go index 5b0e513778..a8bc1e60c9 100644 --- a/flytectl/cmd/get/named_entity.go +++ b/flytectl/cmd/get/named_entity.go @@ -1,7 +1,7 @@ package get import ( - "github.com/lyft/flytectl/printer" + "github.com/lyft/flytectl/pkg/printer" ) var entityColumns = []printer.Column{ diff --git a/flytectl/cmd/get/project.go b/flytectl/cmd/get/project.go index 46868b4932..93c3395d8e 100644 --- a/flytectl/cmd/get/project.go +++ b/flytectl/cmd/get/project.go @@ -9,7 +9,7 @@ import ( "github.com/lyft/flytectl/cmd/config" cmdCore "github.com/lyft/flytectl/cmd/core" - "github.com/lyft/flytectl/printer" + "github.com/lyft/flytectl/pkg/printer" ) var projectColumns = []printer.Column{ @@ -28,10 +28,12 @@ func ProjectToProtoMessages(l []*admin.Project) []proto.Message { func getProjectsFunc(ctx context.Context, args []string, cmdCtx cmdCore.CommandContext) error { adminPrinter := printer.Printer{} - + projects, err := cmdCtx.AdminClient().ListProjects(ctx, &admin.ProjectListRequest{}) + if err != nil { + return err + } if len(args) == 1 { name := args[0] - projects, err := cmdCtx.AdminClient().ListProjects(ctx, &admin.ProjectListRequest{}) if err != nil { return err } @@ -47,10 +49,6 @@ func getProjectsFunc(ctx context.Context, args []string, cmdCtx cmdCore.CommandC } return nil } - projects, err := cmdCtx.AdminClient().ListProjects(ctx, &admin.ProjectListRequest{}) - if err != nil { - return err - } logger.Debugf(ctx, "Retrieved %v projects", len(projects.Projects)) return adminPrinter.Print(config.GetConfig().MustOutputFormat(), projectColumns, ProjectToProtoMessages(projects.Projects)...) } diff --git a/flytectl/cmd/get/task.go b/flytectl/cmd/get/task.go index 08344462ba..a29d690660 100644 --- a/flytectl/cmd/get/task.go +++ b/flytectl/cmd/get/task.go @@ -2,12 +2,11 @@ package get import ( "context" - "github.com/golang/protobuf/proto" "github.com/lyft/flytestdlib/logger" - "github.com/lyft/flytectl/adminutils" - "github.com/lyft/flytectl/printer" + "github.com/lyft/flytectl/pkg/adminutils" + "github.com/lyft/flytectl/pkg/printer" "github.com/lyft/flytectl/cmd/config" cmdCore "github.com/lyft/flytectl/cmd/core" @@ -45,7 +44,7 @@ func getTaskFunc(ctx context.Context, args []string, cmdCtx cmdCore.CommandConte }, // TODO Sorting and limits should be parameters SortBy: &admin.Sort{ - Key: "created_at", + Key: "created_at", Direction: admin.Sort_DESCENDING, }, Limit: 100, diff --git a/flytectl/cmd/get/workflow.go b/flytectl/cmd/get/workflow.go index e20e9be560..71b08f4c7b 100644 --- a/flytectl/cmd/get/workflow.go +++ b/flytectl/cmd/get/workflow.go @@ -2,14 +2,13 @@ package get import ( "context" - "github.com/golang/protobuf/proto" "github.com/lyft/flytestdlib/logger" - "github.com/lyft/flytectl/adminutils" "github.com/lyft/flytectl/cmd/config" cmdCore "github.com/lyft/flytectl/cmd/core" - "github.com/lyft/flytectl/printer" + "github.com/lyft/flytectl/pkg/adminutils" + "github.com/lyft/flytectl/pkg/printer" "github.com/lyft/flyteidl/gen/pb-go/flyteidl/admin" ) @@ -39,7 +38,7 @@ func getWorkflowFunc(ctx context.Context, args []string, cmdCtx cmdCore.CommandC }, // TODO Sorting and limits should be parameters SortBy: &admin.Sort{ - Key: "created_at", + Key: "created_at", Direction: admin.Sort_DESCENDING, }, Limit: 100, diff --git a/flytectl/cmd/root.go b/flytectl/cmd/root.go index f099db0117..11593ba3c0 100644 --- a/flytectl/cmd/root.go +++ b/flytectl/cmd/root.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/lyft/flytectl/cmd/get" - "github.com/lyft/flytectl/printer" + "github.com/lyft/flytectl/pkg/printer" stdConfig "github.com/lyft/flytestdlib/config" "github.com/lyft/flytestdlib/config/viper" @@ -34,7 +34,6 @@ func newRootCmd() *cobra.Command { rootCmd.PersistentFlags().StringVarP(&(config.GetConfig().Project), "project", "p", "", "Specifies the Flyte project.") rootCmd.PersistentFlags().StringVarP(&(config.GetConfig().Domain), "domain", "d", "", "Specifies the Flyte project's domain.") rootCmd.PersistentFlags().StringVarP(&(config.GetConfig().Output), "output", "o", printer.OutputFormatTABLE.String(), fmt.Sprintf("Specifies the output type - supported formats %s", printer.OutputFormats())) - rootCmd.AddCommand(viper.GetConfigCommand()) rootCmd.AddCommand(versionCmd) rootCmd.AddCommand(get.CreateGetCommand()) diff --git a/flytectl/config.yaml b/flytectl/config.yaml index 82a73dac7d..a6b72e5741 100644 --- a/flytectl/config.yaml +++ b/flytectl/config.yaml @@ -1,8 +1,8 @@ admin: # For GRPC endpoints you might want to use dns:///flyte.myexample.com - # endpoint: http://localhost:30082 endpoint: dns:///flyte.lyft.net - insecure: false + # endpoint: dns:///flyte.lyft.net + insecure: true logger: show-source: true level: 1 diff --git a/flytectl/go.mod b/flytectl/go.mod index 593de7c5cf..e7872d901a 100644 --- a/flytectl/go.mod +++ b/flytectl/go.mod @@ -12,6 +12,7 @@ require ( github.com/lyft/flytestdlib v0.3.10-0.20200619054107-45f341b716fa github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mitchellh/mapstructure v1.1.2 + github.com/sirupsen/logrus v1.4.2 github.com/spf13/afero v1.2.2 // indirect github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.5 diff --git a/flytectl/install.sh b/flytectl/install.sh new file mode 100644 index 0000000000..bee7affdf3 --- /dev/null +++ b/flytectl/install.sh @@ -0,0 +1,392 @@ +#!/bin/sh +set -e +# Code generated by godownloader on 2020-10-10T20:07:34Z. DO NOT EDIT. +# + +usage() { + this=$1 + cat </dev/null +} +echoerr() { + echo "$@" 1>&2 +} +log_prefix() { + echo "$0" +} +_logp=6 +log_set_priority() { + _logp="$1" +} +log_priority() { + if test -z "$1"; then + echo "$_logp" + return + fi + [ "$1" -le "$_logp" ] +} +log_tag() { + case $1 in + 0) echo "emerg" ;; + 1) echo "alert" ;; + 2) echo "crit" ;; + 3) echo "err" ;; + 4) echo "warning" ;; + 5) echo "notice" ;; + 6) echo "info" ;; + 7) echo "debug" ;; + *) echo "$1" ;; + esac +} +log_debug() { + log_priority 7 || return 0 + echoerr "$(log_prefix)" "$(log_tag 7)" "$@" +} +log_info() { + log_priority 6 || return 0 + echoerr "$(log_prefix)" "$(log_tag 6)" "$@" +} +log_err() { + log_priority 3 || return 0 + echoerr "$(log_prefix)" "$(log_tag 3)" "$@" +} +log_crit() { + log_priority 2 || return 0 + echoerr "$(log_prefix)" "$(log_tag 2)" "$@" +} +uname_os() { + os=$(uname -s | tr '[:upper:]' '[:lower:]') + case "$os" in + cygwin_nt*) os="windows" ;; + mingw*) os="windows" ;; + msys_nt*) os="windows" ;; + esac + echo "$os" +} +uname_arch() { + arch=$(uname -m) + case $arch in + x86_64) arch="amd64" ;; + x86) arch="386" ;; + i686) arch="386" ;; + i386) arch="386" ;; + aarch64) arch="arm64" ;; + armv5*) arch="armv5" ;; + armv6*) arch="armv6" ;; + armv7*) arch="armv7" ;; + esac + echo ${arch} +} +uname_os_check() { + os=$(uname_os) + case "$os" in + darwin) return 0 ;; + dragonfly) return 0 ;; + freebsd) return 0 ;; + linux) return 0 ;; + android) return 0 ;; + nacl) return 0 ;; + netbsd) return 0 ;; + openbsd) return 0 ;; + plan9) return 0 ;; + solaris) return 0 ;; + windows) return 0 ;; + esac + log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib" + return 1 +} +uname_arch_check() { + arch=$(uname_arch) + case "$arch" in + 386) return 0 ;; + amd64) return 0 ;; + arm64) return 0 ;; + armv5) return 0 ;; + armv6) return 0 ;; + armv7) return 0 ;; + ppc64) return 0 ;; + ppc64le) return 0 ;; + mips) return 0 ;; + mipsle) return 0 ;; + mips64) return 0 ;; + mips64le) return 0 ;; + s390x) return 0 ;; + amd64p32) return 0 ;; + esac + log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib" + return 1 +} +untar() { + tarball=$1 + case "${tarball}" in + *.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" ;; + *.tar) tar --no-same-owner -xf "${tarball}" ;; + *.zip) unzip "${tarball}" ;; + *) + log_err "untar unknown archive format for ${tarball}" + return 1 + ;; + esac +} +http_download_curl() { + local_file=$1 + source_url=$2 + header=$3 + if [ -z "$header" ]; then + code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url") + else + code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url") + fi + if [ "$code" != "200" ]; then + log_debug "http_download_curl received HTTP status $code" + return 1 + fi + return 0 +} +http_download_wget() { + local_file=$1 + source_url=$2 + header=$3 + if [ -z "$header" ]; then + wget -q -O "$local_file" "$source_url" + else + wget -q --header "$header" -O "$local_file" "$source_url" + fi +} +http_download() { + log_debug "http_download $2" + if is_command curl; then + http_download_curl "$@" + return + elif is_command wget; then + http_download_wget "$@" + return + fi + log_crit "http_download unable to find wget or curl" + return 1 +} +http_copy() { + tmp=$(mktemp) + http_download "${tmp}" "$1" "$2" || return 1 + body=$(cat "$tmp") + rm -f "${tmp}" + echo "$body" +} +github_release() { + owner_repo=$1 + version=$2 + test -z "$version" && version="latest" + giturl="https://github.com/${owner_repo}/releases/${version}" + json=$(http_copy "$giturl" "Accept:application/json") + test -z "$json" && return 1 + version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//') + test -z "$version" && return 1 + echo "$version" +} +hash_sha256() { + TARGET=${1:-/dev/stdin} + if is_command gsha256sum; then + hash=$(gsha256sum "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command sha256sum; then + hash=$(sha256sum "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command shasum; then + hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command openssl; then + hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f a + else + log_crit "hash_sha256 unable to find command to compute sha-256 hash" + return 1 + fi +} +hash_sha256_verify() { + TARGET=$1 + checksums=$2 + if [ -z "$checksums" ]; then + log_err "hash_sha256_verify checksum file not specified in arg2" + return 1 + fi + BASENAME=${TARGET##*/} + want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1) + if [ -z "$want" ]; then + log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'" + return 1 + fi + got=$(hash_sha256 "$TARGET") + if [ "$want" != "$got" ]; then + log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got" + return 1 + fi +} +cat /dev/null <