Skip to content

Commit

Permalink
fix: behaviour and add dry-run flag
Browse files Browse the repository at this point in the history
  • Loading branch information
LucaLanziani committed Oct 18, 2023
1 parent 7852814 commit e797a25
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 54 deletions.
4 changes: 0 additions & 4 deletions assets/knative/service.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ kind: Service
metadata:
name: {{ .Name }}
namespace: default
labels:
{{ if .PrivateService }}
networking.knative.dev/visibility: cluster-local
{{ end }}
spec:
template:
spec:
Expand Down
3 changes: 1 addition & 2 deletions src/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ func (c *icli) init(cCtx *cli.Context) error {
registry := cCtx.String(repoNameFlag)
imagePullSecrets := cCtx.StringSlice(imagePullSecretsFlag)
dockerFileName := cCtx.String(dockerFileNameFlag)
isPublicService := cCtx.Bool(isPublicServiceFlag)

if dockerFileName == "" {
dockerFileName = defaults.GeneratedDockerFile
Expand All @@ -95,7 +94,7 @@ func (c *icli) init(cCtx *cli.Context) error {
projectDirectory,
cCtx.String(runtimeVersionFlag),
version,
isPublicService,
cCtx.Bool(isPrivateServiceFlag),
imagePullSecrets,
c.Resources,
)
Expand Down
59 changes: 49 additions & 10 deletions src/cli/deploy.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,79 @@
package cli

import (
"fmt"

"github.com/nearform/initium-cli/src/services/git"
knative "github.com/nearform/initium-cli/src/services/k8s"
"github.com/urfave/cli/v2"
)

func (c *icli) Deploy(cCtx *cli.Context) error {
config, err := knative.Config(
cCtx.String(endpointFlag),
cCtx.String(tokenFlag),
[]byte(cCtx.String(caCRTFlag)),
)
namespace := cCtx.String(namespaceFlag)
envFile := cCtx.String(envVarFileFlag)

project, err := c.getProject(cCtx)
if err != nil {
return err
}
project, err := c.getProject(cCtx)

commitSha, err := git.GetHash()
if err != nil {
return err
}

commitSha, err := git.GetHash()
serviceManifest, err := knative.LoadManifest(namespace, commitSha, project, c.dockerImage, envFile)
if err != nil {
return err
}

return knative.Apply(cCtx.String(namespaceFlag), commitSha, config, project, c.dockerImage, cCtx.String(envVarFileFlag))
if cCtx.Bool(dryRunFlag) {
yamlBytes, err := knative.ToYaml(serviceManifest)
if err != nil {
return err
}
fmt.Fprintf(c.Writer, "%s", yamlBytes)
return nil
}

config, err := knative.Config(
cCtx.String(endpointFlag),
cCtx.String(tokenFlag),
[]byte(cCtx.String(caCRTFlag)),
)

if err != nil {
return err
}

return knative.Apply(serviceManifest, config)
}

func (c icli) DeployCMD() *cli.Command {
flags := c.CommandFlags([]FlagsType{Kubernetes, Shared})

flags = append(flags, &cli.BoolFlag{
Name: dryRunFlag,
Usage: "print out the knative manifest without applying it",
Value: false,
})

return &cli.Command{
Name: "deploy",
Usage: "deploy the application as a knative service",
Flags: c.CommandFlags([]FlagsType{Kubernetes, Shared}),
Flags: flags,
Action: c.Deploy,
Before: c.baseBeforeFunc,
Before: func(ctx *cli.Context) error {
if err := c.loadFlagsFromConfig(ctx); err != nil {
return err
}

ignoredFlags := []string{}
if ctx.Bool(dryRunFlag) {
ignoredFlags = append(ignoredFlags, endpointFlag, tokenFlag, caCRTFlag)
}

return c.checkRequiredFlags(ctx, ignoredFlags)
},
}
}
28 changes: 19 additions & 9 deletions src/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"

"github.com/nearform/initium-cli/src/services/git"
Expand Down Expand Up @@ -46,7 +47,8 @@ const (
stopOnBuildFlag string = "stop-on-build"
stopOnPushFlag string = "stop-on-push"
envVarFileFlag string = "env-var-file"
isPublicServiceFlag string = "public"
isPrivateServiceFlag string = "private"
dryRunFlag string = "dry-run"
)

type flags struct {
Expand Down Expand Up @@ -110,6 +112,12 @@ func InitFlags() flags {
Required: false,
Category: "deploy",
},
&cli.BoolFlag{
Name: isPrivateServiceFlag,
Usage: "Do not expose the service public endpoint",
Category: "init",
Value: false,
},
&cli.StringFlag{
Name: envVarFileFlag,
Value: defaults.EnvVarFile,
Expand Down Expand Up @@ -157,12 +165,6 @@ func InitFlags() flags {
Value: defaults.ConfigFile,
EnvVars: []string{"INITIUM_CONFIG_FILE"},
},
&cli.BoolFlag{
Name: isPublicServiceFlag,
Usage: "will deploy the service as accessible from outside of the cluster",
Category: "init",
Value: false,
},
},
Shared: []cli.Flag{
&cli.StringFlag{
Expand Down Expand Up @@ -257,9 +259,17 @@ func (c icli) loadFlagsFromConfig(ctx *cli.Context) error {
for _, name := range v.Names() {
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
switch config[name].(type) {
case bool:
if err := ctx.Set(mainName, strconv.FormatBool(config[name].(bool))); err != nil {
return err
}
default:
if err := ctx.Set(mainName, config[name].(string)); err != nil {
return err
}
}

}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/cli/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ import (
"github.com/charmbracelet/log"
)

func compareConfig(t *testing.T, appName string, registry string, isPublicService bool, writer io.Writer) {
func compareConfig(t *testing.T, appName string, registry string, isPrivateService bool, writer io.Writer) {
configTemplate := fmt.Sprintf(`app-name: %s
container-registry: %s
default-branch: main
dockerfile-name: null
env-var-file: .env.initium
image-pull-secrets: null
public: %t
private: %t
runtime-version: null
`,
appName,
registry,
isPublicService,
isPrivateService,
)

result := fmt.Sprint(writer.(*bytes.Buffer))
Expand Down
53 changes: 40 additions & 13 deletions src/services/k8s/knative.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ import (
"github.com/nearform/initium-cli/src/services/docker"
"github.com/nearform/initium-cli/src/services/project"
"github.com/nearform/initium-cli/src/utils/defaults"
"sigs.k8s.io/yaml"

corev1 "k8s.io/api/core/v1"
apimachineryErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
Expand All @@ -30,6 +33,8 @@ import (
const (
UpdateShaAnnotationName = "initium.nearform.com/updateSha"
UpdateTimestampAnnotationName = "initium.nearform.com/updateTimestamp"
visibilityLabel = "networking.knative.dev/visibility"
visibilityLabelPrivateValue = "cluster-local"
)

func Config(endpoint string, token string, caCrt []byte) (*rest.Config, error) {
Expand All @@ -46,7 +51,16 @@ func Config(endpoint string, token string, caCrt []byte) (*rest.Config, error) {
}, nil
}

func loadManifest(namespace string, commitSha string, project *project.Project, dockerImage docker.DockerImage, envFile string) (*servingv1.Service, error) {
func setLabels(manifest *servingv1.Service, project project.Project) {
if manifest.ObjectMeta.Labels == nil {
manifest.ObjectMeta.Labels = map[string]string{}
}
if project.IsPrivate {
manifest.ObjectMeta.Labels[visibilityLabel] = visibilityLabelPrivateValue
}
}

func LoadManifest(namespace string, commitSha string, project *project.Project, dockerImage docker.DockerImage, envFile string) (*servingv1.Service, error) {
knativeTemplate := path.Join("assets", "knative", "service.yaml.tmpl")
template, err := template.ParseFS(project.Resources, knativeTemplate)
if err != nil {
Expand All @@ -57,7 +71,6 @@ func loadManifest(namespace string, commitSha string, project *project.Project,
"Name": dockerImage.Name,
"RemoteTag": dockerImage.RemoteTag(),
"ImagePullSecrets": project.ImagePullSecrets,
"PrivateService": !project.IsPublicService,
}

output := &bytes.Buffer{}
Expand Down Expand Up @@ -91,16 +104,24 @@ func loadManifest(namespace string, commitSha string, project *project.Project,
UpdateTimestampAnnotationName: time.Now().Format(time.RFC3339),
}

envVarList, err := loadEnvFile(envFile)
if err != nil {
setLabels(service, *project)
if err = setEnv(service, envFile); err != nil {
return nil, err
}

service.Spec.Template.Spec.Containers[0].Env = append(service.Spec.Template.Spec.Containers[0].Env, envVarList...)

return service, nil
}

func setEnv(manifest *servingv1.Service, envFile string) error {
envVarList, err := loadEnvFile(envFile)
if err != nil {
return err
}

manifest.Spec.Template.Spec.Containers[0].Env = append(manifest.Spec.Template.Spec.Containers[0].Env, envVarList...)
return nil
}

func loadEnvFile(envFile string) ([]corev1.EnvVar, error) {
var envVarList []corev1.EnvVar
if _, err := os.Stat(envFile); err != nil {
Expand Down Expand Up @@ -157,15 +178,21 @@ func loadEnvFile(envFile string) ([]corev1.EnvVar, error) {
return envVarList, nil
}

func Apply(namespace string, commitSha string, config *rest.Config, project *project.Project, dockerImage docker.DockerImage, envFile string) error {
log.Info("Deploying Knative service", "host", config.Host, "name", project.Name, "namespace", namespace)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5)
defer cancel()

serviceManifest, err := loadManifest(namespace, commitSha, project, dockerImage, envFile)
func ToYaml(serviceManifest *servingv1.Service) ([]byte, error) {
scheme := runtime.NewScheme()
servingv1.AddToScheme(scheme)
codec := serializer.NewCodecFactory(scheme).LegacyCodec(servingv1.SchemeGroupVersion)
jsonBytes, err := runtime.Encode(codec, serviceManifest)
if err != nil {
return err
return nil, err
}
return yaml.JSONToYAML(jsonBytes)
}

func Apply(serviceManifest *servingv1.Service, config *rest.Config) error {
log.Info("Deploying Knative service", "host", config.Host, "name", serviceManifest.ObjectMeta.Name, "namespace", serviceManifest.ObjectMeta.Namespace)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5)
defer cancel()

// Create a new Knative Serving client
servingClient, err := servingv1client.NewForConfig(config)
Expand Down
21 changes: 11 additions & 10 deletions src/services/k8s/knative_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ func TestLoadManifestForPrivateService(t *testing.T) {
commitSha := "93f4be93"

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

dockerImage := docker.DockerImage{
Expand All @@ -72,7 +72,7 @@ func TestLoadManifestForPrivateService(t *testing.T) {
Tag: "v1.1.0",
}

serviceManifest, err := loadManifest(namespace, commitSha, proj, dockerImage, path.Join(root, "example/.env.sample"))
serviceManifest, err := LoadManifest(namespace, commitSha, proj, dockerImage, path.Join(root, "example/.env.sample"))

if err != nil {
t.Fatalf(fmt.Sprintf("Error: %v", err))
Expand All @@ -82,8 +82,9 @@ func TestLoadManifestForPrivateService(t *testing.T) {
assert.Assert(t, annotations[UpdateTimestampAnnotationName] != "", "Missing %s annotation", UpdateTimestampAnnotationName)
assert.Assert(t, annotations[UpdateShaAnnotationName] == commitSha, "Expected %s SHA, got %s", commitSha, annotations[UpdateShaAnnotationName])

labels := serviceManifest.ObjectMeta.Labels
assert.Assert(t, labels["networking.knative.dev/visibility"] == "cluster-local", "Missing networking.knative.dev/visibility label with cluster-local value")
labels := serviceManifest.GetLabels()
_, ok := labels[visibilityLabel]
assert.Assert(t, !ok, "Visibility label should not be set for public services")
}

func TestLoadManifestForPublicService(t *testing.T) {
Expand All @@ -95,7 +96,7 @@ func TestLoadManifestForPublicService(t *testing.T) {
Directory: path.Join(root, "example"),
Resources: os.DirFS(root),
ImagePullSecrets: imagePullSecrets,
IsPublicService: true,
IsPrivate: true,
}

dockerImage := docker.DockerImage{
Expand All @@ -105,7 +106,7 @@ func TestLoadManifestForPublicService(t *testing.T) {
Tag: "v1.1.0",
}

serviceManifest, err := loadManifest(namespace, commitSha, proj, dockerImage, path.Join(root, "example/.env.sample"))
serviceManifest, err := LoadManifest(namespace, commitSha, proj, dockerImage, path.Join(root, "example/.env.sample"))

if err != nil {
t.Fatalf(fmt.Sprintf("Error: %v", err))
Expand All @@ -117,6 +118,6 @@ func TestLoadManifestForPublicService(t *testing.T) {
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)

labels := serviceManifest.ObjectMeta.Labels
assert.Assert(t, labels["networking.knative.dev/visibility"] == "", "Label networking.knative.dev/visibility should not be present")
labels := serviceManifest.GetLabels()
assert.Assert(t, labels[visibilityLabel] == visibilityLabelPrivateValue)
}
6 changes: 3 additions & 3 deletions src/services/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type Project struct {
DefaultRuntimeVersion string
ImagePullSecrets []string
Resources fs.FS
IsPublicService bool
IsPrivate bool
}

type InitOptions struct {
Expand All @@ -45,15 +45,15 @@ func GuessAppName() *string {
return &name
}

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

Expand Down

0 comments on commit e797a25

Please sign in to comment.