Skip to content

Commit 0840be5

Browse files
authored
feat: add registry login to CI workflows (knative#3297)
GitHub workflows generated by 'func config ci' previously lacked registry authentication. Now a registry login step was added. Add new flags for registry authentication and runner configuration: - --use-registry-login: enable/disable registry login step (default: true) - --registry-login-url-variable-name: customize registry URL variable (default: REGISTRY_LOGIN_URL) - --registry-user-variable-name: customize username variable (default: REGISTRY_USERNAME) - --registry-pass-secret-name: customize password secret (default: REGISTRY_PASSWORD) - --self-hosted-runner: use self-hosted instead of ubuntu-latest runners (default: false) Workflow generation logic is refactored into helper functions for better maintainability. More logic will be added to this functions in follow up PRs. Issue knative#3256 Signed-off-by: Stanislav Jakuschevskij <[email protected]>
1 parent e2b04d3 commit 0840be5

File tree

4 files changed

+209
-32
lines changed

4 files changed

+209
-32
lines changed

cmd/ci/config.go

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,39 +14,64 @@ const (
1414
DefaultGitHubWorkflowDir = ".github/workflows"
1515
DefaultGitHubWorkflowFilename = "func-deploy.yaml"
1616

17-
WorkflowNameFlag = "workflow-name"
18-
DefaultWorkflowName = "Func Deploy"
19-
2017
BranchFlag = "branch"
2118
DefaultBranch = "main"
2219

20+
WorkflowNameFlag = "workflow-name"
21+
DefaultWorkflowName = "Func Deploy"
22+
2323
KubeconfigSecretNameFlag = "kubeconfig-secret-name"
2424
DefaultKubeconfigSecretName = "KUBECONFIG"
2525

26+
RegistryLoginUrlVariableNameFlag = "registry-login-url-variable-name"
27+
DefaultRegistryLoginUrlVariableName = "REGISTRY_LOGIN_URL"
28+
29+
RegistryUserVariableNameFlag = "registry-user-variable-name"
30+
DefaultRegistryUserVariableName = "REGISTRY_USERNAME"
31+
32+
RegistryPassSecretNameFlag = "registry-pass-secret-name"
33+
DefaultRegistryPassSecretName = "REGISTRY_PASSWORD"
34+
2635
RegistryUrlVariableNameFlag = "registry-url-variable-name"
2736
DefaultRegistryUrlVariableName = "REGISTRY_URL"
37+
38+
UseRegistryLoginFlag = "use-registry-login"
39+
DefaultUseRegistryLogin = true
40+
41+
UseSelfHostedRunnerFlag = "self-hosted-runner"
42+
DefaultUseSelfHostedRunner = false
2843
)
2944

3045
// CIConfig readonly configuration
3146
type CIConfig struct {
3247
githubWorkflowDir,
3348
githubWorkflowFilename,
3449
path,
35-
workflowName,
3650
branch,
51+
workflowName,
3752
kubeconfigSecret,
53+
registryLoginUrlVar,
54+
registryUserVar,
55+
registryPassSecret,
3856
registryUrlVar string
57+
useRegistryLogin,
58+
useSelfHostedRunner bool
3959
}
4060

4161
func NewCIGitHubConfig() CIConfig {
4262
return CIConfig{
4363
githubWorkflowDir: DefaultGitHubWorkflowDir,
4464
githubWorkflowFilename: DefaultGitHubWorkflowFilename,
4565
path: viper.GetString(PathFlag),
46-
workflowName: viper.GetString(WorkflowNameFlag),
4766
branch: viper.GetString(BranchFlag),
67+
workflowName: viper.GetString(WorkflowNameFlag),
4868
kubeconfigSecret: viper.GetString(KubeconfigSecretNameFlag),
69+
registryLoginUrlVar: viper.GetString(RegistryLoginUrlVariableNameFlag),
70+
registryUserVar: viper.GetString(RegistryUserVariableNameFlag),
71+
registryPassSecret: viper.GetString(RegistryPassSecretNameFlag),
4972
registryUrlVar: viper.GetString(RegistryUrlVariableNameFlag),
73+
useRegistryLogin: viper.GetBool(UseRegistryLoginFlag),
74+
useSelfHostedRunner: viper.GetBool(UseSelfHostedRunnerFlag),
5075
}
5176
}
5277

@@ -70,10 +95,30 @@ func (cc *CIConfig) Branch() string {
7095
return cc.branch
7196
}
7297

98+
func (cc *CIConfig) UseRegistryLogin() bool {
99+
return cc.useRegistryLogin
100+
}
101+
102+
func (cc *CIConfig) UseSelfHostedRunner() bool {
103+
return cc.useSelfHostedRunner
104+
}
105+
73106
func (cc *CIConfig) KubeconfigSecret() string {
74107
return cc.kubeconfigSecret
75108
}
76109

110+
func (cc *CIConfig) RegistryLoginUrlVar() string {
111+
return cc.registryLoginUrlVar
112+
}
113+
114+
func (cc *CIConfig) RegistryUserVar() string {
115+
return cc.registryUserVar
116+
}
117+
118+
func (cc *CIConfig) RegistryPassSecret() string {
119+
return cc.registryPassSecret
120+
}
121+
77122
func (cc *CIConfig) RegistryUrlVar() string {
78123
return cc.registryUrlVar
79124
}

cmd/ci/workflow.go

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,30 +34,15 @@ type step struct {
3434
}
3535

3636
func NewGitHubWorkflow(conf CIConfig) *githubWorkflow {
37-
runsOn := "ubuntu-latest"
38-
39-
pushTrigger := newPushTrigger(conf.Branch())
37+
runsOn := createRunsOn(conf.UseSelfHostedRunner())
38+
pushTrigger := createPushTrigger(conf.Branch())
4039

4140
var steps []step
42-
checkoutCode := newStep("Checkout code").
43-
withUses("actions/checkout@v4")
44-
steps = append(steps, *checkoutCode)
45-
46-
setupK8Context := newStep("Setup Kubernetes context").
47-
withUses("azure/k8s-set-context@v4").
48-
withActionConfig("method", "kubeconfig").
49-
withActionConfig("kubeconfig", newSecret(conf.KubeconfigSecret()))
50-
steps = append(steps, *setupK8Context)
51-
52-
installFuncCli := newStep("Install func cli").
53-
withUses("gauron99/knative-func-action@main").
54-
withActionConfig("version", "knative-v1.19.1").
55-
withActionConfig("name", "func")
56-
steps = append(steps, *installFuncCli)
57-
58-
deployFunc := newStep("Deploy function").
59-
withRun("func deploy --registry=" + newVariable(conf.RegistryUrlVar()) + " -v")
60-
steps = append(steps, *deployFunc)
41+
steps = createCheckoutStep(steps)
42+
steps = createK8ContextStep(conf, steps)
43+
steps = createRegistryLoginStep(conf, steps)
44+
steps = createFuncCLIInstallStep(steps)
45+
steps = createFuncDeployStep(conf, steps)
6146

6247
return &githubWorkflow{
6348
Name: conf.WorkflowName(),
@@ -71,14 +56,72 @@ func NewGitHubWorkflow(conf CIConfig) *githubWorkflow {
7156
}
7257
}
7358

74-
func newPushTrigger(branch string) workflowTriggers {
59+
func createRunsOn(useSelfHostedRunner bool) string {
60+
runsOn := "ubuntu-latest"
61+
if useSelfHostedRunner {
62+
runsOn = "self-hosted"
63+
}
64+
return runsOn
65+
}
66+
67+
func createPushTrigger(branch string) workflowTriggers {
7568
result := workflowTriggers{
7669
Push: &pushTrigger{Branches: []string{branch}},
7770
}
7871

7972
return result
8073
}
8174

75+
func createCheckoutStep(steps []step) []step {
76+
checkoutCode := newStep("Checkout code").
77+
withUses("actions/checkout@v4")
78+
79+
return append(steps, *checkoutCode)
80+
}
81+
82+
func createK8ContextStep(conf CIConfig, steps []step) []step {
83+
setupK8Context := newStep("Setup Kubernetes context").
84+
withUses("azure/k8s-set-context@v4").
85+
withActionConfig("method", "kubeconfig").
86+
withActionConfig("kubeconfig", newSecret(conf.KubeconfigSecret()))
87+
88+
return append(steps, *setupK8Context)
89+
}
90+
91+
func createRegistryLoginStep(conf CIConfig, steps []step) []step {
92+
if !conf.UseRegistryLogin() {
93+
return steps
94+
}
95+
96+
loginToContainerRegistry := newStep("Login to container registry").
97+
withUses("docker/login-action@v3").
98+
withActionConfig("registry", newVariable(conf.RegistryLoginUrlVar())).
99+
withActionConfig("username", newVariable(conf.RegistryUserVar())).
100+
withActionConfig("password", newSecret(conf.RegistryPassSecret()))
101+
102+
return append(steps, *loginToContainerRegistry)
103+
}
104+
105+
func createFuncDeployStep(conf CIConfig, steps []step) []step {
106+
registryUrl := newVariable(conf.RegistryUrlVar())
107+
if conf.UseRegistryLogin() {
108+
registryUrl = newVariable(conf.RegistryLoginUrlVar()) + "/" + newVariable(conf.RegistryUserVar())
109+
}
110+
deployFunc := newStep("Deploy function").
111+
withRun("func deploy --registry=" + registryUrl + " -v")
112+
113+
return append(steps, *deployFunc)
114+
}
115+
116+
func createFuncCLIInstallStep(steps []step) []step {
117+
installFuncCli := newStep("Install func cli").
118+
withUses("gauron99/knative-func-action@main").
119+
withActionConfig("version", "knative-v1.19.1").
120+
withActionConfig("name", "func")
121+
122+
return append(steps, *installFuncCli)
123+
}
124+
82125
func newStep(name string) *step {
83126
return &step{Name: name}
84127
}

cmd/config_ci.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@ func NewConfigCICmd(loaderSaver common.FunctionLoaderSaver, writer ci.WorkflowWr
1515
Short: "Generate a GitHub Workflow for function deployment",
1616
PreRunE: bindEnv(
1717
ci.PathFlag,
18+
ci.UseRegistryLoginFlag,
19+
ci.UseSelfHostedRunnerFlag,
1820
ci.WorkflowNameFlag,
1921
ci.BranchFlag,
2022
ci.KubeconfigSecretNameFlag,
23+
ci.RegistryLoginUrlVariableNameFlag,
24+
ci.RegistryUserVariableNameFlag,
25+
ci.RegistryPassSecretNameFlag,
2126
ci.RegistryUrlVariableNameFlag,
2227
),
2328
RunE: func(cmd *cobra.Command, args []string) (err error) {
@@ -27,6 +32,18 @@ func NewConfigCICmd(loaderSaver common.FunctionLoaderSaver, writer ci.WorkflowWr
2732

2833
addPathFlag(cmd)
2934

35+
cmd.Flags().Bool(
36+
ci.UseRegistryLoginFlag,
37+
ci.DefaultUseRegistryLogin,
38+
"Add a registry login step in the github workflow",
39+
)
40+
41+
cmd.Flags().Bool(
42+
ci.UseSelfHostedRunnerFlag,
43+
ci.DefaultUseSelfHostedRunner,
44+
"Use a 'self-hosted' runner instead of the default 'ubuntu-latest' for local runner execution",
45+
)
46+
3047
cmd.Flags().String(
3148
ci.WorkflowNameFlag,
3249
ci.DefaultWorkflowName,
@@ -45,6 +62,24 @@ func NewConfigCICmd(loaderSaver common.FunctionLoaderSaver, writer ci.WorkflowWr
4562
"Use a custom secret name in the workflow, e.g. secret.YOUR_CUSTOM_KUBECONFIG",
4663
)
4764

65+
cmd.Flags().String(
66+
ci.RegistryLoginUrlVariableNameFlag,
67+
ci.DefaultRegistryLoginUrlVariableName,
68+
"Use a custom registry login url variable name in the workflow, e.g. vars.YOUR_REGISTRY_LOGIN_URL",
69+
)
70+
71+
cmd.Flags().String(
72+
ci.RegistryUserVariableNameFlag,
73+
ci.DefaultRegistryUserVariableName,
74+
"Use a custom registry user variable name in the workflow, e.g. vars.YOUR_REGISTRY_USER",
75+
)
76+
77+
cmd.Flags().String(
78+
ci.RegistryPassSecretNameFlag,
79+
ci.DefaultRegistryPassSecretName,
80+
"Use a custom registry pass secret name in the workflow, e.g. secret.YOUR_REGISTRY_PASSWORD",
81+
)
82+
4883
cmd.Flags().String(
4984
ci.RegistryUrlVariableNameFlag,
5085
ci.DefaultRegistryUrlVariableName,

cmd/config_ci_test.go

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,46 @@ func TestNewConfigCICmd_WritesWorkflowFile(t *testing.T) {
5151
}
5252

5353
func TestNewConfigCICmd_WorkflowYAMLHasCorrectStructure(t *testing.T) {
54+
result := runConfigCiCmd(t, unitTestOpts())
55+
56+
assert.NilError(t, result.executeErr)
57+
assertDefaultWorkflow(t, result.gwYamlString)
58+
}
59+
60+
func TestNewConfigCICmd_WorkflowYAMLHasCustomValues(t *testing.T) {
61+
// GIVEN
5462
opts := unitTestOpts()
63+
opts.args = append(opts.args,
64+
"--self-hosted-runner",
65+
"--workflow-name=Custom Deploy",
66+
"--kubeconfig-secret-name=DEV_CLUSTER_KUBECONFIG",
67+
"--registry-login-url-variable-name=DEV_REGISTRY_LOGIN_URL",
68+
"--registry-user-variable-name=DEV_REGISTRY_USER",
69+
"--registry-pass-secret-name=DEV_REGISTRY_PASS",
70+
"--branch=master",
71+
)
72+
73+
// WHEN
5574
result := runConfigCiCmd(t, opts)
5675

76+
// THEN
5777
assert.NilError(t, result.executeErr)
58-
assertDefaultWorkflow(t, result.gwYamlString)
78+
assertCustomWorkflow(t, result.gwYamlString)
79+
}
80+
81+
func TestNewConfigCICmd_WorkflowHasNoRegistryLogin(t *testing.T) {
82+
// GIVEN
83+
opts := unitTestOpts()
84+
opts.args = append(opts.args, "--use-registry-login=false")
85+
86+
// WHEN
87+
result := runConfigCiCmd(t, opts)
88+
89+
// THEN
90+
assert.NilError(t, result.executeErr)
91+
assert.Assert(t, !strings.Contains(result.gwYamlString, "docker/login-action@v3"))
92+
assert.Assert(t, !strings.Contains(result.gwYamlString, "Login to container registry"))
93+
assert.Assert(t, yamlContains(result.gwYamlString, "--registry=${{ vars.REGISTRY_URL }}"))
5994
}
6095

6196
// ---------------------
@@ -239,7 +274,10 @@ func runConfigCiCmd(
239274
// including the default values which can be changed:
240275
// - runs-on: ubuntu-latest
241276
// - kubeconfig: ${{ secrets.KUBECONFIG }}
242-
// - run: func deploy --registry=${{ vars.REGISTRY_URL }} -v
277+
// - registry: ${{ vars.REGISTRY_LOGIN_URL }}")
278+
// - username: ${{ vars.REGISTRY_USERNAME }}
279+
// - password: ${{ secrets.REGISTRY_PASSWORD }}
280+
// - run: func deploy --registry=${{ vars.REGISTRY_LOGIN_URL }}/${{ vars.REGISTRY_USERNAME }} -v
243281
func assertDefaultWorkflow(t *testing.T, actualGw string) {
244282
t.Helper()
245283

@@ -248,7 +286,7 @@ func assertDefaultWorkflow(t *testing.T, actualGw string) {
248286

249287
assert.Assert(t, yamlContains(actualGw, "ubuntu-latest"))
250288

251-
assert.Assert(t, strings.Count(actualGw, "- name:") == 4)
289+
assert.Assert(t, strings.Count(actualGw, "- name:") == 5)
252290

253291
assert.Assert(t, yamlContains(actualGw, "Checkout code"))
254292
assert.Assert(t, yamlContains(actualGw, "actions/checkout@v4"))
@@ -258,13 +296,19 @@ func assertDefaultWorkflow(t *testing.T, actualGw string) {
258296
assert.Assert(t, yamlContains(actualGw, "method: kubeconfig"))
259297
assert.Assert(t, yamlContains(actualGw, "kubeconfig: ${{ secrets.KUBECONFIG }}"))
260298

299+
assert.Assert(t, yamlContains(actualGw, "Login to container registry"))
300+
assert.Assert(t, yamlContains(actualGw, "docker/login-action@v3"))
301+
assert.Assert(t, yamlContains(actualGw, "registry: ${{ vars.REGISTRY_LOGIN_URL }}"))
302+
assert.Assert(t, yamlContains(actualGw, "username: ${{ vars.REGISTRY_USERNAME }}"))
303+
assert.Assert(t, yamlContains(actualGw, "password: ${{ secrets.REGISTRY_PASSWORD }}"))
304+
261305
assert.Assert(t, yamlContains(actualGw, "Install func cli"))
262306
assert.Assert(t, yamlContains(actualGw, "gauron99/knative-func-action@main"))
263307
assert.Assert(t, yamlContains(actualGw, "version: knative-v1.19.1"))
264308
assert.Assert(t, yamlContains(actualGw, "name: func"))
265309

266310
assert.Assert(t, yamlContains(actualGw, "Deploy function"))
267-
assert.Assert(t, yamlContains(actualGw, "func deploy --registry=${{ vars.REGISTRY_URL }} -v"))
311+
assert.Assert(t, yamlContains(actualGw, "func deploy --registry=${{ vars.REGISTRY_LOGIN_URL }}/${{ vars.REGISTRY_USERNAME }} -v"))
268312
}
269313

270314
func yamlContains(yaml, substr string) cmp.Comparison {
@@ -278,5 +322,15 @@ func yamlContains(yaml, substr string) cmp.Comparison {
278322
}
279323
}
280324

325+
func assertCustomWorkflow(t *testing.T, actualGw string) {
326+
assert.Assert(t, yamlContains(actualGw, "Custom Deploy"))
327+
assert.Assert(t, yamlContains(actualGw, "self-hosted"))
328+
assert.Assert(t, yamlContains(actualGw, "DEV_CLUSTER_KUBECONFIG"))
329+
assert.Assert(t, yamlContains(actualGw, "DEV_REGISTRY_LOGIN_URL"))
330+
assert.Assert(t, yamlContains(actualGw, "DEV_REGISTRY_USER"))
331+
assert.Assert(t, yamlContains(actualGw, "DEV_REGISTRY_PASS"))
332+
assert.Assert(t, yamlContains(actualGw, "- master"))
333+
}
334+
281335
// ----------------------
282336
// END: Testing Framework

0 commit comments

Comments
 (0)