Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 45 additions & 14 deletions cmd/ci/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,71 @@ import (
const (
ConfigCIFeatureFlag = "FUNC_ENABLE_CI_CONFIG"

DefaultGithubWorkflowDir = ".github/workflows"
DefaultGithubWorkflowFilename = "func-deploy.yaml"
PathFlag = "path"

PathOption = "path"
DefaultGitHubWorkflowDir = ".github/workflows"
DefaultGitHubWorkflowFilename = "func-deploy.yaml"

WorkflowNameOption = "workflow-name"
WorkflowNameFlag = "workflow-name"
DefaultWorkflowName = "Func Deploy"

BranchFlag = "branch"
DefaultBranch = "main"

KubeconfigSecretNameFlag = "kubeconfig-secret-name"
DefaultKubeconfigSecretName = "KUBECONFIG"

RegistryUrlVariableNameFlag = "registry-url-variable-name"
DefaultRegistryUrlVariableName = "REGISTRY_URL"
)

// CIConfig readonly CI configuration
// CIConfig readonly configuration
type CIConfig struct {
githubWorkflowDir,
githubWorkflowFilename,
path,
workflowName string
workflowName,
branch,
kubeconfigSecret,
registryUrlVar string
}

func NewCiGithubConfig() CIConfig {
func NewCIGitHubConfig() CIConfig {
return CIConfig{
githubWorkflowDir: DefaultGithubWorkflowDir,
githubWorkflowFilename: DefaultGithubWorkflowFilename,
path: viper.GetString(PathOption),
workflowName: viper.GetString(WorkflowNameOption),
githubWorkflowDir: DefaultGitHubWorkflowDir,
githubWorkflowFilename: DefaultGitHubWorkflowFilename,
path: viper.GetString(PathFlag),
workflowName: viper.GetString(WorkflowNameFlag),
branch: viper.GetString(BranchFlag),
kubeconfigSecret: viper.GetString(KubeconfigSecretNameFlag),
registryUrlVar: viper.GetString(RegistryUrlVariableNameFlag),
}
}

func (cc *CIConfig) FnGithubWorkflowDir(fnRoot string) string {
func (cc *CIConfig) FnGitHubWorkflowDir(fnRoot string) string {
return filepath.Join(fnRoot, cc.githubWorkflowDir)
}

func (cc *CIConfig) FnGithubWorkflowFilepath(fnRoot string) string {
return filepath.Join(cc.FnGithubWorkflowDir(fnRoot), cc.githubWorkflowFilename)
func (cc *CIConfig) FnGitHubWorkflowFilepath(fnRoot string) string {
return filepath.Join(cc.FnGitHubWorkflowDir(fnRoot), cc.githubWorkflowFilename)
}

func (cc *CIConfig) Path() string {
return cc.path
}

func (cc *CIConfig) WorkflowName() string {
return cc.workflowName
}

func (cc *CIConfig) Branch() string {
return cc.branch
}

func (cc *CIConfig) KubeconfigSecret() string {
return cc.kubeconfigSecret
}

func (cc *CIConfig) RegistryUrlVar() string {
return cc.registryUrlVar
}
134 changes: 134 additions & 0 deletions cmd/ci/workflow.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package ci

import (
"bytes"
"fmt"

"gopkg.in/yaml.v3"
)

type githubWorkflow struct {
Name string `yaml:"name"`
On workflowTriggers `yaml:"on"`
Jobs map[string]job `yaml:"jobs"`
}

type workflowTriggers struct {
Push *pushTrigger `yaml:"push,omitempty"`
}

type pushTrigger struct {
Branches []string `yaml:"branches,omitempty"`
}

type job struct {
RunsOn string `yaml:"runs-on"`
Steps []step `yaml:"steps"`
}

type step struct {
Name string `yaml:"name,omitempty"`
Uses string `yaml:"uses,omitempty"`
Run string `yaml:"run,omitempty"`
With map[string]string `yaml:"with,omitempty"`
}

func NewGitHubWorkflow(conf CIConfig) *githubWorkflow {
runsOn := "ubuntu-latest"

pushTrigger := newPushTrigger(conf.Branch())

var steps []step
checkoutCode := newStep("Checkout code").
withUses("actions/checkout@v4")
steps = append(steps, *checkoutCode)

setupK8Context := newStep("Setup Kubernetes context").
withUses("azure/k8s-set-context@v4").
withActionConfig("method", "kubeconfig").
withActionConfig("kubeconfig", newSecret(conf.KubeconfigSecret()))
steps = append(steps, *setupK8Context)

installFuncCli := newStep("Install func cli").
withUses("gauron99/knative-func-action@main").
withActionConfig("version", "knative-v1.19.1").
withActionConfig("name", "func")
steps = append(steps, *installFuncCli)

deployFunc := newStep("Deploy function").
withRun("func deploy --registry=" + newVariable(conf.RegistryUrlVar()) + " -v")
steps = append(steps, *deployFunc)

return &githubWorkflow{
Name: conf.WorkflowName(),
On: pushTrigger,
Jobs: map[string]job{
"deploy": {
RunsOn: runsOn,
Steps: steps,
},
},
}
}

func newPushTrigger(branch string) workflowTriggers {
result := workflowTriggers{
Push: &pushTrigger{Branches: []string{branch}},
}

return result
}

func newStep(name string) *step {
return &step{Name: name}
}

func (s *step) withUses(u string) *step {
s.Uses = u
return s
}

func (s *step) withRun(r string) *step {
s.Run = r
return s
}

func (s *step) withActionConfig(key, value string) *step {
if s.With == nil {
s.With = make(map[string]string)
}

s.With[key] = value

return s
}

func newSecret(key string) string {
return fmt.Sprintf("${{ secrets.%s }}", key)
}

func newVariable(key string) string {
return fmt.Sprintf("${{ vars.%s }}", key)
}

func (gw *githubWorkflow) Export(path string, w WorkflowWriter) error {
raw, err := gw.toYaml()
if err != nil {
return err
}

return w.Write(path, raw)
}

func (gw *githubWorkflow) toYaml() ([]byte, error) {
var buf bytes.Buffer
encoder := yaml.NewEncoder(&buf)
encoder.SetIndent(2)

if err := encoder.Encode(gw); err != nil {
return nil, err
}
encoder.Close()

return buf.Bytes(), nil
}
22 changes: 22 additions & 0 deletions cmd/ci/workflow_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ci_test

import (
"strings"
"testing"

"gotest.tools/v3/assert"
"knative.dev/func/cmd/ci"
)

func TestGitHubWorkflow_Export(t *testing.T) {
// GIVEN
gw := ci.NewGitHubWorkflow(ci.NewCIGitHubConfig())
bufferWriter := ci.NewBufferWriter()

// WHEN
exportErr := gw.Export("path", bufferWriter)

// THEN
assert.NilError(t, exportErr, "unexpected error when exporting GitHub Workflow")
assert.Assert(t, strings.Contains(bufferWriter.Buffer.String(), gw.Name))
}
45 changes: 45 additions & 0 deletions cmd/ci/writer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ci

import (
"bytes"
"os"
"path/filepath"
)

const (
dirPerm = 0755 // o: rwx, g|u: r-x
filePerm = 0644 // o: rw, g|u: r
)

var DefaultWorkflowWriter = &fileWriter{}

type WorkflowWriter interface {
Write(path string, p []byte) error
}

type fileWriter struct{}

func (fw *fileWriter) Write(path string, p []byte) error {
if err := os.MkdirAll(filepath.Dir(path), dirPerm); err != nil {
return err
}

if err := os.WriteFile(path, p, filePerm); err != nil {
return err
}

return nil
}

type bufferWriter struct {
Buffer *bytes.Buffer
}

func NewBufferWriter() *bufferWriter {
return &bufferWriter{Buffer: &bytes.Buffer{}}
}

func (bw *bufferWriter) Write(_ string, p []byte) error {
_, err := bw.Buffer.Write(p)
return err
}
30 changes: 30 additions & 0 deletions cmd/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,33 @@ func (s standardLoaderSaver) Load(path string) (fn.Function, error) {
func (s standardLoaderSaver) Save(f fn.Function) error {
return f.Write()
}

// NewMockLoaderSaver creates a MockLoaderSaver with default no-op
// implementations.
func NewMockLoaderSaver() *MockLoaderSaver {
return &MockLoaderSaver{
LoadFn: func(path string) (fn.Function, error) {
return fn.Function{}, nil
},
SaveFn: func(f fn.Function) error {
return nil
},
}
}

// MockLoaderSaver provides configurable function loading and saving for testing
// purposes.
type MockLoaderSaver struct {
LoadFn func(path string) (fn.Function, error)
SaveFn func(f fn.Function) error
}

// Load invokes the configured LoadFn to load a function from the given path.
func (m MockLoaderSaver) Load(path string) (fn.Function, error) {
return m.LoadFn(path)
}

// Save invokes the configured SaveFn to persist the given function.
func (m MockLoaderSaver) Save(f fn.Function) error {
return m.SaveFn(f)
}
13 changes: 9 additions & 4 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ package cmd

import (
"fmt"
"os"

"github.com/AlecAivazis/survey/v2"
"github.com/ory/viper"
"github.com/spf13/cobra"

"knative.dev/func/cmd/ci"
"knative.dev/func/cmd/common"
"knative.dev/func/pkg/config"
fn "knative.dev/func/pkg/functions"
)

func NewConfigCmd(loadSaver common.FunctionLoaderSaver, newClient ClientFactory) *cobra.Command {
func NewConfigCmd(loaderSaver common.FunctionLoaderSaver, writer ci.WorkflowWriter, newClient ClientFactory) *cobra.Command {
cmd := &cobra.Command{
Use: "config",
Short: "Configure a function",
Expand All @@ -35,10 +37,13 @@ or from the directory specified with --path.
addVerboseFlag(cmd, cfg.Verbose)

cmd.AddCommand(NewConfigGitCmd(newClient))
cmd.AddCommand(NewConfigLabelsCmd(loadSaver))
cmd.AddCommand(NewConfigEnvsCmd(loadSaver))
cmd.AddCommand(NewConfigLabelsCmd(loaderSaver))
cmd.AddCommand(NewConfigEnvsCmd(loaderSaver))
cmd.AddCommand(NewConfigVolumesCmd())
cmd.AddCommand(NewConfigCICmd(loadSaver))

if os.Getenv(ci.ConfigCIFeatureFlag) == "true" {
cmd.AddCommand(NewConfigCICmd(loaderSaver, writer))
}

return cmd
}
Expand Down
Loading
Loading