Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: private registry with multiple secrets #130

Merged
merged 8 commits into from
Oct 14, 2023
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
4 changes: 4 additions & 0 deletions assets/knative/service.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ metadata:
spec:
template:
spec:
imagePullSecrets:
{{ range .ImagePullSecrets }}
- name: {{ . }}
{{ end }}
containers:
- image: {{ .RemoteTag }}
imagePullPolicy: Always
15 changes: 7 additions & 8 deletions src/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type icli struct {
CWD string
DockerService docker.DockerService
Logger *log.Logger
project project.Project
project *project.Project
dockerImage docker.DockerImage
flags flags
Writer io.Writer
Expand Down Expand Up @@ -67,6 +67,7 @@ func (c *icli) init(cCtx *cli.Context) error {
projectDirectory := cCtx.String(projectDirectoryFlag)
absProjectDirectory, err := filepath.Abs(cCtx.String(projectDirectoryFlag))
registry := cCtx.String(repoNameFlag)
imagePullSecrets := cCtx.StringSlice(imagePullSecretsFlag)
dockerFileName := cCtx.String(dockerFileNameFlag)

if dockerFileName == "" {
Expand All @@ -83,6 +84,7 @@ func (c *icli) init(cCtx *cli.Context) error {
projectDirectory,
cCtx.String(runtimeVersionFlag),
version,
imagePullSecrets,
c.Resources,
)

Expand All @@ -109,18 +111,18 @@ func (c *icli) init(cCtx *cli.Context) error {

c.DockerService = dockerService
c.dockerImage = dockerImage
c.project = project
c.project = &project
return nil
}

func (c *icli) getProject(cCtx *cli.Context) (*project.Project, error) {
if (c.project == project.Project{}) {
if c.project == nil {
err := c.init(cCtx)
if err != nil {
return nil, err
}
}
return &c.project, nil
return c.project, nil
}

func (c icli) Run(args []string) error {
Expand All @@ -143,10 +145,7 @@ func (c icli) Run(args []string) error {
return err
}

if err := c.checkRequiredFlags(ctx, []string{}); err != nil {
return err
}
return nil
return c.checkRequiredFlags(ctx, []string{})
},
}

Expand Down
33 changes: 22 additions & 11 deletions src/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const (
dockerFileNameFlag string = "dockerfile-name"
configFileFlag string = "config-file"
namespaceFlag string = "namespace"
imagePullSecretsFlag string = "image-pull-secrets"
stopOnBuildFlag string = "stop-on-build"
stopOnPushFlag string = "stop-on-push"
envVarFileFlag string = "env-var-file"
Expand Down Expand Up @@ -101,6 +102,13 @@ func InitFlags() flags {
Required: true,
Category: "deploy",
},
&cli.StringSliceFlag{
Name: imagePullSecretsFlag,
Usage: "Define one or more (repeating the flag or in csv format for the environment variable) image pull secrets",
EnvVars: []string{"INITIUM_IMAGE_PULL_SECRETS"},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is splitting by , by default?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's implemented by the library

Required: false,
Category: "deploy",
},
&cli.StringFlag{
Name: envVarFileFlag,
Value: defaults.EnvVarFile,
Expand Down Expand Up @@ -184,12 +192,14 @@ func InitFlags() flags {
// this is an hack to go around that issue, we should remove it once urfave v3 is released
for _, vs := range f.all {
for _, flag := range vs {
stringFlag := flag.(*cli.StringFlag)
if stringFlag.Required {
f.requiredFlags = append(f.requiredFlags, stringFlag.Name)
stringFlag.Required = false
switch flag.(type) {
case *cli.StringFlag:
stringFlag := flag.(*cli.StringFlag)
if stringFlag.Required {
f.requiredFlags = append(f.requiredFlags, stringFlag.Name)
stringFlag.Required = false
}
}

}
}

Expand All @@ -202,7 +212,6 @@ func (c icli) checkRequiredFlags(ctx *cli.Context, ignoredFlags []string) error

for _, v := range ctx.Command.Flags {
name := v.Names()[0]
c.Logger.Debugf("%s is set to %s", name, ctx.String(name))
if slices.Contains(requiredFlags, name) && !slices.Contains(ignoredFlags, name) && !ctx.IsSet(name) {
missingFlags = append(missingFlags, name)
}
Expand Down Expand Up @@ -233,14 +242,16 @@ func (c icli) loadFlagsFromConfig(ctx *cli.Context) error {
return fmt.Errorf("cannot parse config file %s, not a valid yaml", cfgFile)
}

excludedFlags := excludedFlagsFromConfig()

c.Logger.Debugf("Loading flags %v", ctx.Command)
for _, v := range ctx.Command.Flags {
mainName := v.Names()[0]
for _, name := range v.Names() {
c.Logger.Debugf("%s is set to %s", name, ctx.String(name))
if name != "help" && !ctx.IsSet(mainName) {
if config[name] != nil {
c.Logger.Debugf("Loading %s as %s", name, config[name])
ctx.Set(mainName, config[name].(string))
c.Logger.Debugf("%s is set = %v", name, ctx.IsSet(name))
if name != "help" && !slices.Contains(excludedFlags, name) && config[name] != nil && !ctx.IsSet(mainName) {
if err := ctx.Set(mainName, config[name].(string)); err != nil {
return err
}
}
}
Expand Down
68 changes: 48 additions & 20 deletions src/cli/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"path/filepath"
"sort"
"strings"

"github.com/charmbracelet/log"
"k8s.io/utils/strings/slices"
Expand Down Expand Up @@ -41,8 +42,8 @@ func (c icli) InitGithubCMD(cCtx *cli.Context) error {
return nil
}

func (c icli) InitConfigCMD(ctx *cli.Context) error {
excludedFlags := []string{
func excludedFlagsFromConfig() []string {
return []string{
"help",
appVersionFlag,
namespaceFlag,
Expand All @@ -52,35 +53,45 @@ func (c icli) InitConfigCMD(ctx *cli.Context) error {
tokenFlag,
registryPasswordFlag,
caCRTFlag,
registryUserFlag,
endpointFlag,
}
}

func (c icli) InitConfigCMD(ctx *cli.Context) error {
excludedFlags := excludedFlagsFromConfig()

f := []cli.Flag{}
for _, vs := range c.flags.all {
f = append(f, vs...)
}

sort.Sort(cli.FlagsByName(f))

var n, v string
config := ""
for _, flag := range f {
stringFlag := flag.(*cli.StringFlag)
if slices.Contains(excludedFlags, stringFlag.Name) {
continue
}

value := ctx.String(stringFlag.Name)
if value == "" {
value = stringFlag.Value
switch flag.(type) {
case *cli.StringFlag:
stringFlag := flag.(*cli.StringFlag)
if slices.Contains(excludedFlags, stringFlag.Name) {
continue
}

n = stringFlag.Name
v = ctx.String(stringFlag.Name)
case *cli.StringSliceFlag:
stringSliceFlag := flag.(*cli.StringSliceFlag)
if slices.Contains(excludedFlags, stringSliceFlag.Name) {
continue
}
n = stringSliceFlag.Name
v = strings.Join(ctx.StringSlice(stringSliceFlag.Name), ",")
}

next := ""
if value == "" {
next = fmt.Sprintf("%s: null\n", stringFlag.Name)
} else {
next = fmt.Sprintf("%s: %s\n", stringFlag.Name, value)
if v == "" {
v = "null"
}

config = config + next
config = config + fmt.Sprintf("%s: %s\n", n, v)
}

if ctx.Bool(persistFlag) {
Expand All @@ -102,7 +113,16 @@ func (c icli) InitServiceAccountCMD(ctx *cli.Context) error {
}

func (c icli) InitCMD() *cli.Command {
configFlags := c.CommandFlags([]FlagsType{Shared})
ef := excludedFlagsFromConfig()
configFlags := []cli.Flag{}
for _, vs := range c.flags.all {
for _, flag := range vs {
if !slices.Contains(ef, flag.Names()[0]) {
configFlags = append(configFlags, flag)
}
}
}

configFlags = append(configFlags, &cli.BoolFlag{
Name: persistFlag,
Value: false,
Expand All @@ -125,7 +145,15 @@ func (c icli) InitCMD() *cli.Command {
Usage: "create a config file with all available flags set to null",
Flags: configFlags,
Action: c.InitConfigCMD,
Before: c.baseBeforeFunc,
Before: func(ctx *cli.Context) error {
if err := c.loadFlagsFromConfig(ctx); err != nil {
return err
}

ignoredFlags := excludedFlagsFromConfig()

return c.checkRequiredFlags(ctx, ignoredFlags)
},
},
{
Name: "service-account",
Expand Down
3 changes: 1 addition & 2 deletions src/cli/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ import (

func compareConfig(t *testing.T, appName string, registry string, writer io.Writer) {
configTemplate := fmt.Sprintf(`app-name: %s
cluster-endpoint: null
container-registry: %s
default-branch: main
dockerfile-name: null
env-var-file: .env.initium
registry-user: null
image-pull-secrets: null
runtime-version: null
`,
appName,
Expand Down
8 changes: 7 additions & 1 deletion src/services/k8s/knative.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,15 @@ func loadManifest(namespace string, commitSha string, project *project.Project,
return nil, fmt.Errorf("error reading the knative service yaml: %v", err)
}

templateParams := map[string]interface{}{
"Name": dockerImage.Name,
"RemoteTag": dockerImage.RemoteTag(),
"ImagePullSecrets": project.ImagePullSecrets,
}

output := &bytes.Buffer{}
// TODO replace map[string]string{} with proper values
if err = template.Execute(output, dockerImage); err != nil {
if err = template.Execute(output, templateParams); err != nil {
return nil, err
}

Expand Down
12 changes: 8 additions & 4 deletions src/services/k8s/knative_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package k8s
import (
"encoding/base64"
"fmt"
"gotest.tools/v3/assert"
"os"
"path"
"testing"

"gotest.tools/v3/assert"

"github.com/nearform/initium-cli/src/services/docker"
"github.com/nearform/initium-cli/src/services/project"
)
Expand Down Expand Up @@ -57,10 +58,12 @@ func TestConfig(t *testing.T) {
func TestLoadManifest(t *testing.T) {
namespace := "custom"
commitSha := "93f4be93"
imagePullSecrets := []string{"secretPassword123"}

proj := &project.Project{Name: "knative_test",
Directory: path.Join(root, "example"),
Resources: os.DirFS(root),
Directory: path.Join(root, "example"),
Resources: os.DirFS(root),
ImagePullSecrets: imagePullSecrets,
}

dockerImage := docker.DockerImage{
Expand All @@ -77,7 +80,8 @@ func TestLoadManifest(t *testing.T) {
}

annotations := serviceManifest.Spec.Template.ObjectMeta.Annotations
pullSecret := serviceManifest.Spec.Template.Spec.ImagePullSecrets[0].Name
assert.Assert(t, annotations[UpdateTimestampAnnotationName] != "", "Missing %s annotation", UpdateTimestampAnnotationName)
assert.Assert(t, annotations[UpdateShaAnnotationName] == commitSha, "Expected %s SHA, got %s", commitSha, annotations[UpdateShaAnnotationName])
assert.Assert(t, pullSecret == imagePullSecrets[0], "Expected secret value to be %s, got %s", imagePullSecrets, pullSecret)
}

14 changes: 8 additions & 6 deletions src/services/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Project struct {
Directory string
RuntimeVersion string
DefaultRuntimeVersion string
ImagePullSecrets []string
Resources fs.FS
}

Expand All @@ -43,13 +44,14 @@ func GuessAppName() *string {
return &name
}

func New(name string, directory string, runtimeVersion string, version string, resources fs.FS) Project {
func New(name string, directory string, runtimeVersion string, version string, imagePullSecrets []string, resources fs.FS) Project {
return Project{
Name: name,
Directory: directory,
RuntimeVersion: runtimeVersion,
Resources: resources,
Version: version,
Name: name,
Directory: directory,
RuntimeVersion: runtimeVersion,
ImagePullSecrets: imagePullSecrets,
Resources: resources,
Version: version,
}
}

Expand Down