Skip to content

Commit

Permalink
feat: Pipelines as Code - initial support (pack)
Browse files Browse the repository at this point in the history
Signed-off-by: Zbynek Roubalik <zroubalik@gmail.com>
  • Loading branch information
zroubalik committed Mar 2, 2023
1 parent 850adf3 commit eba54e2
Show file tree
Hide file tree
Showing 1,004 changed files with 118,690 additions and 15,744 deletions.
18 changes: 14 additions & 4 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ func (s standardLoaderSaver) Save(f fn.Function) error {

var defaultLoaderSaver standardLoaderSaver

func NewConfigCmd(loadSaver functionLoaderSaver) *cobra.Command {
func NewConfigCmd(loadSaver functionLoaderSaver, newClient ClientFactory) *cobra.Command {
cmd := &cobra.Command{
Use: "config",
Short: "Configure a function",
Long: `Configure a function
Interactive prompt that allows configuration of Volume mounts, Environment
Interactive prompt that allows configuration of Git configuration, Volume mounts, Environment
variables, and Labels for a function project present in the current directory
or from the directory specified with --path.
`,
Expand All @@ -65,6 +65,7 @@ or from the directory specified with --path.
addPathFlag(cmd)
addVerboseFlag(cmd, cfg.Verbose)

cmd.AddCommand(NewConfigGitCmd(newClient))
cmd.AddCommand(NewConfigLabelsCmd(loadSaver))
cmd.AddCommand(NewConfigEnvsCmd(loadSaver))
cmd.AddCommand(NewConfigVolumesCmd())
Expand All @@ -84,8 +85,8 @@ func runConfigCmd(cmd *cobra.Command, args []string) (err error) {
Name: "selectedConfig",
Prompt: &survey.Select{
Message: "What do you want to configure?",
Options: []string{"Environment variables", "Volumes", "Labels"},
Default: "Environment variables",
Options: []string{"Git", "Environment variables", "Volumes", "Labels"},
Default: "Git",
},
},
{
Expand Down Expand Up @@ -117,6 +118,9 @@ func runConfigCmd(cmd *cobra.Command, args []string) (err error) {
} else if answers.SelectedConfig == "Labels" {
err = runAddLabelsPrompt(cmd.Context(), function, defaultLoaderSaver)
}
// } else if answers.SelectedConfig == "Git" {
// printGitStatus(function)
// }
case "Remove":
if answers.SelectedConfig == "Volumes" {
err = runRemoveVolumesPrompt(function)
Expand All @@ -125,6 +129,9 @@ func runConfigCmd(cmd *cobra.Command, args []string) (err error) {
} else if answers.SelectedConfig == "Labels" {
err = runRemoveLabelsPrompt(function, defaultLoaderSaver)
}
// } else if answers.SelectedConfig == "Git" {
// printGitStatus(function)
// }
case "List":
if answers.SelectedConfig == "Volumes" {
listVolumes(function)
Expand All @@ -133,6 +140,9 @@ func runConfigCmd(cmd *cobra.Command, args []string) (err error) {
} else if answers.SelectedConfig == "Labels" {
listLabels(function)
}
// } else if answers.SelectedConfig == "Git" {
// printGitStatus(function)
// }
}

return
Expand Down
62 changes: 62 additions & 0 deletions cmd/config_git.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"

"knative.dev/func/pkg/config"
fn "knative.dev/func/pkg/functions"
)

func NewConfigGitCmd(newClient ClientFactory) *cobra.Command {
cmd := &cobra.Command{
Use: "git",
Short: "Manage Git configuration of a function",
Long: `Manage Git configuration of a function
Prints Git configuration for a function project present in
the current directory or from the directory specified with --path.
`,
SuggestFor: []string{"gti", "Git", "Gti"},
PreRunE: bindEnv("path"),
RunE: func(cmd *cobra.Command, args []string) (err error) {
return runConfigGitCmd(cmd, newClient)
},
}
// Global Config
cfg, err := config.NewDefault()
if err != nil {
fmt.Fprintf(cmd.OutOrStdout(), "error loading config at '%v'. %v\n", config.File(), err)
}

// Function Context
f, _ := fn.NewFunction(effectivePath())
if f.Initialized() {
cfg = cfg.Apply(f)
}

configGitSetCmd := NewConfigGitSetCmd(newClient)
//configGitRemoveCmd := NewConfigGitRemoveCmd()

addPathFlag(cmd)
addVerboseFlag(cmd, cfg.Verbose)

cmd.AddCommand(configGitSetCmd)

return cmd
}

func runConfigGitCmd(cmd *cobra.Command, newClient ClientFactory) (err error) {
// TODO implement, this is just a mock:
// fmt.Printf("--------------------------- Function Git config ---------------------------\n")
// fmt.Printf("STATUS\t PROPERTY\tVALUE\n")
// fmt.Printf(" ✅\t Git URL:\t%s\n", f.Build.Git.URL)
// fmt.Printf(" ❌\t Builder:\t%s\n", f.Build.Builder)
// fmt.Printf(" \t\t\t - Function configuration has %q but generated Pipelines have \"s2i\", please regenerate.\n\n", f.Build.Builder)
// fmt.Printf(" ✅\t Image: \t%s\n", f.Image)
// fmt.Printf("\n")
// end of mock

return nil
}
259 changes: 259 additions & 0 deletions cmd/config_git_set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
package cmd

import (
"fmt"

"github.com/AlecAivazis/survey/v2"
"github.com/ory/viper"
"github.com/spf13/cobra"
"knative.dev/func/pkg/config"
fn "knative.dev/func/pkg/functions"
"knative.dev/func/pkg/pipelines"
)

func NewConfigGitSetCmd(newClient ClientFactory) *cobra.Command {
cmd := &cobra.Command{
Use: "set",
Short: "Set Git settings in the function configuration",
Long: `Set Git settings in the function configuration
Interactive prompt to set Git settings in the function project in the current
directory or from the directory specified with --path.
`,
SuggestFor: []string{"add", "ad", "update", "create", "insert", "append"},
PreRunE: bindEnv("path", "builder", "builder-image", "image", "registry"),
RunE: func(cmd *cobra.Command, args []string) (err error) {
return runConfigGitSetCmd(cmd, newClient)
},
}

// Global Config
cfg, err := config.NewDefault()
if err != nil {
fmt.Fprintf(cmd.OutOrStdout(), "error loading config at '%v'. %v\n", config.File(), err)
}

// Function Context
f, _ := fn.NewFunction(effectivePath())
if f.Initialized() {
cfg = cfg.Apply(f)
}

// Flags
//
// Globally-Configurable Flags:
// Options whose value may be defined globally may also exist on the
// contextually relevant function; but sets are flattened via cfg.Apply(f)
cmd.Flags().StringP("builder", "b", cfg.Builder,
fmt.Sprintf("Builder to use when creating the function's container. Currently supported builders are %s.", KnownBuilders()))
cmd.Flags().StringP("registry", "r", cfg.Registry,
"Container registry + registry namespace. (ex 'ghcr.io/myuser'). The full image name is automatically determined using this along with function name. (Env: $FUNC_REGISTRY)")
cmd.Flags().StringP("namespace", "n", cfg.Namespace,
"Deploy into a specific namespace. Will use function's current namespace by default if already deployed, and the currently active namespace if it can be determined. (Env: $FUNC_NAMESPACE)")

// Function-Context Flags:
// Options whose value is avaolable on the function with context only
// (persisted but not globally configurable)
builderImage := f.Build.BuilderImages[f.Build.Builder]
cmd.Flags().StringP("builder-image", "", builderImage,
"Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)")
cmd.Flags().StringP("image", "i", f.Image, "Full image name in the form [registry]/[namespace]/[name]:[tag]@[digest]. This option takes precedence over --registry. Specifying digest is optional, but if it is given, 'build' and 'push' phases are disabled. (Env: $FUNC_IMAGE)")

// TODO enable flags
// cmd.Flags().StringP("git-url", "g", f.Build.Git.URL,
// "Repo url to push the code to be built (Env: $FUNC_GIT_URL)")
// cmd.Flags().StringP("git-branch", "t", f.Build.Git.Revision,
// "Git revision (branch) to be used when deploying via a git repository (Env: $FUNC_GIT_BRANCH)")
// cmd.Flags().StringP("git-dir", "d", f.Build.Git.ContextDir,
// "Directory in the repo to find the function (default is the root) (Env: $FUNC_GIT_DIR)")
// cmd.Flags().BoolP("remote", "", f.Deploy.Remote,
// "Trigger a remote deployment. Default is to deploy and build from the local system (Env: $FUNC_REMOTE)")

addPathFlag(cmd)
addVerboseFlag(cmd, cfg.Verbose)

return cmd
}

type configGitSetConfig struct {
buildConfig // further embeds config.Global

Namespace string

GitURL string
GitRevision string
GitContextDir string

WebhookTrigger bool
WebhookTriggerSet bool // whether WebhookTrigger value has been set
WebhookTriggerAutoConfig bool // whether to configure WebhookTrigger automatically

metadata pipelines.PacMetadata
}

// newConfigGitSetConfig creates a buildConfig populated from command flags and
// environment variables; in that precedence.
func newConfigGitSetConfig(cmd *cobra.Command) (c configGitSetConfig) {
c = configGitSetConfig{
buildConfig: newBuildConfig(),
// TODO enable flags
// GitBranch: viper.GetString("git-branch"),
// GitDir: viper.GetString("git-dir"),
// GitURL: viper.GetString("git-url"),

Namespace: viper.GetString("namespace"),

metadata: pipelines.PacMetadata{
// TODO create cli args to specify whether to configure only specific options
ConfigureLocalResources: true,
ConfigureClusterResources: true,
ConfigureRemoteResources: true,
},
}

return c
}

func (c configGitSetConfig) Prompt(f fn.Function) (configGitSetConfig, error) {
var err error
if c.buildConfig, err = c.buildConfig.Prompt(); err != nil {
return c, err
}

// prompt if git URL hasn't been set previously
if c.GitURL == "" {
// TODO we can try to read git url from the local .git settings
url := f.Build.Git.URL
if err := survey.AskOne(&survey.Input{
Message: "The URL to Git repository with the function source code:",
Default: url,
}, &url, survey.WithValidator(survey.Required)); err != nil {
return c, err
}
c.GitURL = url
}

// prompt if git revision hasn't been set previously
if c.GitRevision == "" {
// TODO we can try to read git url from the local .git settings
revision := f.Build.Git.Revision
if err := survey.AskOne(&survey.Input{
Message: "The Git branch or tag we are targeting:",
Help: "ie: main, refs/tags/*",
Default: revision,
}, &revision); err != nil {
return c, err
}
c.GitRevision = revision
}

// prompt if contextDir hasn't been set previously
if c.GitContextDir == "" {
contextDir := f.Build.Git.ContextDir
if err := survey.AskOne(&survey.Input{
Message: "A subpath within the repository:",
Help: "A subpath within the repository where the source code of a function is located.",
Default: contextDir,
}, &contextDir); err != nil {
return c, err
}
c.GitContextDir = contextDir
}

// prompt if webhook trigger setting hasn't been set previously
if !c.WebhookTriggerSet {
trigger := true
if err := survey.AskOne(&survey.Confirm{
Message: "Do you want to configure webhook trigger?",
Help: "Webhook trigger also running pipeline on a git event, ie: commit, push",
Default: trigger,
}, &trigger, survey.WithValidator(survey.Required)); err != nil {
return c, err
}
c.WebhookTrigger = trigger
c.WebhookTriggerSet = true
}

if c.WebhookTrigger {
// prompt if PersonalAccessToken hasn't been set previously
if c.metadata.PersonalAccessToken == "" {
var personalAccessToken string
if err := survey.AskOne(&survey.Password{
Message: "Please enter the GitHub access token:",
}, &personalAccessToken, survey.WithValidator(survey.Required)); err != nil {
return c, err
}
c.metadata.PersonalAccessToken = personalAccessToken
}

// TODO prompt here if user want to configure remote webhook automatically (default)
// or manauly - print neccesary info then
// ie: c.WebhookTriggerAutoConfig
}

return c, nil
}

func (c configGitSetConfig) Validate(cmd *cobra.Command) (err error) {
// Bubble validation
if err = c.buildConfig.Validate(); err != nil {
return
}

return
}

// Configure the given function. Updates a function struct with all
// configurable values. Note that the config already includes function's
// current values, as they were passed through via flag defaults.
func (c configGitSetConfig) Configure(f fn.Function) (fn.Function, error) {
var err error

// Bubble configure request
//
// The member values on the config object now take absolute precidence
// because they include 1) static config 2) user's global config
// 3) Environment variables and 4) flag values (which were set with their
// default being 1-3).
f = c.buildConfig.Configure(f) // also configures .buildConfig.Global

// Configure basic members
f.Build.Git.URL = c.GitURL
f.Build.Git.ContextDir = c.GitContextDir
f.Build.Git.Revision = c.GitRevision // TODO: should match; perhaps "refSpec"

// Save the function which has now been updated with flags/config
if err = f.Write(); err != nil { // TODO: remove when client API uses 'f'
return f, err
}

return f, nil
}

func runConfigGitSetCmd(cmd *cobra.Command, newClient ClientFactory) (err error) {
var (
cfg configGitSetConfig
f fn.Function
)
if err = config.CreatePaths(); err != nil { // for possible auth.json usage
return
}
cfg = newConfigGitSetConfig(cmd)
if f, err = fn.NewFunction(cfg.Path); err != nil {
return
}
if cfg, err = cfg.Prompt(f); err != nil {
return
}
if err = cfg.Validate(cmd); err != nil {
return
}
if f, err = cfg.Configure(f); err != nil { // Updates f with deploy cfg
return
}

client, done := newClient(ClientConfig{Namespace: cfg.Namespace, Verbose: cfg.Verbose}, fn.WithRegistry(cfg.Registry))
defer done()

return client.ConfigurePAC(cmd.Context(), f, cfg.metadata)
}
Loading

0 comments on commit eba54e2

Please sign in to comment.