From a9cbf10fcae1bfaf3993f119de87ccdb0a97d0b9 Mon Sep 17 00:00:00 2001 From: Alexander Jung Date: Fri, 24 Nov 2023 10:56:21 +0100 Subject: [PATCH 1/5] feat(build): Expose `Workdir` in `BuildOptions` Whilst the CLI accepts the first positional argument as the working directory, the internal method `Build` should have a cleaner way to expose this, and this is achieved in this commit without changing the existing CLI functionality. Signed-off-by: Alexander Jung --- internal/cli/kraft/build/build.go | 12 ++++++------ .../cli/kraft/build/builder_kraftfile_unikraft.go | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/cli/kraft/build/build.go b/internal/cli/kraft/build/build.go index 2a51c2f1c..4089bb969 100644 --- a/internal/cli/kraft/build/build.go +++ b/internal/cli/kraft/build/build.go @@ -41,9 +41,9 @@ type BuildOptions struct { Rootfs string `long:"rootfs" usage:"Specify a path to use as root file system (can be volume or initramfs)"` SaveBuildLog string `long:"build-log" usage:"Use the specified file to save the output from the build"` Target string `long:"target" short:"t" usage:"Build a particular known target"` + Workdir string `noattribute:"true"` project app.Application - workdir string } // Build a Unikraft unikernel. @@ -96,16 +96,16 @@ func (opts *BuildOptions) Pre(cmd *cobra.Command, args []string) error { func (opts *BuildOptions) Run(ctx context.Context, args []string) error { var err error if len(args) == 0 { - opts.workdir, err = os.Getwd() + opts.Workdir, err = os.Getwd() if err != nil { return err } - } else { - opts.workdir = args[0] + } else if len(opts.Workdir) == 0 { + opts.Workdir = args[0] } popts := []app.ProjectOption{ - app.WithProjectWorkdir(opts.workdir), + app.WithProjectWorkdir(opts.Workdir), } if len(opts.Kraftfile) > 0 { @@ -178,7 +178,7 @@ func (opts *BuildOptions) Run(ctx context.Context, args []string) error { return fmt.Errorf("could not complete build: %w", err) } - if opts.Rootfs, err = utils.BuildRootfs(ctx, opts.workdir, opts.Rootfs, selected...); err != nil { + if opts.Rootfs, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, selected...); err != nil { return err } diff --git a/internal/cli/kraft/build/builder_kraftfile_unikraft.go b/internal/cli/kraft/build/builder_kraftfile_unikraft.go index 4393cb6b9..b117ff0e9 100644 --- a/internal/cli/kraft/build/builder_kraftfile_unikraft.go +++ b/internal/cli/kraft/build/builder_kraftfile_unikraft.go @@ -116,7 +116,7 @@ func (build *builderKraftfileUnikraft) pull(ctx context.Context, opts *BuildOpti return templatePack.Pull( ctx, pack.WithPullProgressFunc(w), - pack.WithPullWorkdir(opts.workdir), + pack.WithPullWorkdir(opts.Workdir), // pack.WithPullChecksum(!opts.NoChecksum), pack.WithPullCache(!opts.NoCache), pack.WithPullAuthConfig(auths), @@ -243,7 +243,7 @@ func (build *builderKraftfileUnikraft) pull(ctx context.Context, opts *BuildOpti return p.Pull( ctx, pack.WithPullProgressFunc(w), - pack.WithPullWorkdir(opts.workdir), + pack.WithPullWorkdir(opts.Workdir), // pack.WithPullChecksum(!opts.NoChecksum), pack.WithPullCache(!opts.NoCache), pack.WithPullAuthConfig(auths), From 15d4a34e503e1651a240524b2cf5cd1ecb2ac4c8 Mon Sep 17 00:00:00 2001 From: Alexander Jung Date: Fri, 8 Dec 2023 16:42:12 +0100 Subject: [PATCH 2/5] refactor(deploy): Migrate `Prepare` to `Deploy` in `deployer` iface This commit reworks the `deployer` internal interface to abstract the whole mechanism of deploying the inputs to `kraft cloud deploy` to the individual context. This results in a new `Deploy` method as opposed to a `Prepare` method which performs the entire action. Instead of returning the package, which could be in fact an intermediate step, the output is the (list) of instance(s). This greater abstraction provides future implementations to handle different contexts that do not yield intermediate package references which the `Prepare` method previously assumed. The resulting instance(s) are properly output as the result from the new `Deploy` method interface. Signed-off-by: Alexander Jung --- internal/cli/kraft/cloud/deploy/deploy.go | 126 +---------------- internal/cli/kraft/cloud/deploy/deployer.go | 6 +- .../deploy/deployer_kraftfile_runtime.go | 129 +++++++++++++++++- 3 files changed, 135 insertions(+), 126 deletions(-) diff --git a/internal/cli/kraft/cloud/deploy/deploy.go b/internal/cli/kraft/cloud/deploy/deploy.go index 2df8027b9..a48cf0c5a 100644 --- a/internal/cli/kraft/cloud/deploy/deploy.go +++ b/internal/cli/kraft/cloud/deploy/deploy.go @@ -10,7 +10,6 @@ import ( "fmt" "os" "path/filepath" - "strings" "time" "github.com/MakeNowJust/heredoc" @@ -18,15 +17,12 @@ import ( "kraftkit.sh/cmdfactory" "kraftkit.sh/config" - "kraftkit.sh/internal/cli/kraft/cloud/instance/create" "kraftkit.sh/internal/cli/kraft/cloud/utils" "kraftkit.sh/log" "kraftkit.sh/packmanager" - "kraftkit.sh/tui/processtree" "kraftkit.sh/unikraft/app" kraftcloud "sdk.kraft.cloud" - kraftcloudinstances "sdk.kraft.cloud/instances" ) type DeployOptions struct { @@ -39,10 +35,11 @@ type DeployOptions struct { Metro string `noattribute:"true"` Name string `local:"true" long:"name" short:"n" usage:"Name of the deployment"` NoStart bool `local:"true" long:"no-start" short:"S" usage:"Do not start the instance after creation"` - Output string `local:"true" long:"output" short:"o" usage:"Set output format" default:"table"` + Output string `local:"true" long:"output" short:"o" usage:"Set output format"` Ports []string `local:"true" long:"port" short:"p" usage:"Specify the port mapping between external to internal"` Project app.Application `noattribute:"true"` Replicas int `local:"true" long:"replicas" short:"R" usage:"Number of replicas of the instance" default:"0"` + Rootfs string `local:"true" long:"rootfs" usage:"Specify a path to use as root filesystem"` Strategy packmanager.MergeStrategy `noattribute:"true"` SubDomain string `local:"true" long:"subdomain" short:"s" usage:"Set the name to use when provisioning a subdomain"` Timeout time.Duration `local:"true" long:"timeout" usage:"Set the timeout for remote procedure calls"` @@ -173,124 +170,15 @@ func (opts *DeployOptions) Run(ctx context.Context, args []string) error { return fmt.Errorf("could not determine what or how to deploy from the given context") } - var pkgName string - - if len(opts.Name) > 0 { - pkgName = opts.Name - } else if opts.Project != nil && len(opts.Project.Name()) > 0 { - pkgName = opts.Project.Name() - } else { - pkgName = filepath.Base(opts.Workdir) - } - - if strings.HasPrefix(pkgName, "unikraft.io") { - pkgName = "index." + pkgName - } - if !strings.HasPrefix(pkgName, "index.unikraft.io") { - pkgName = fmt.Sprintf( - "index.unikraft.io/%s/%s:latest", - strings.TrimSuffix(strings.TrimPrefix(opts.Auth.User, "robot$"), ".users.kraftcloud"), - pkgName, - ) - } - - packs, err := d.Prepare(ctx, opts, pkgName) + instances, err := d.Deploy(ctx, opts, args...) if err != nil { return fmt.Errorf("could not prepare deployment: %w", err) } - // FIXME(nderjung): Gathering the digest like this really dirty. - metadata := packs[0].Columns() - var digest string - for _, m := range metadata { - if m.Name != "index" { - continue - } - - digest = m.Value - } - - // TODO(nderjung): This is a quirk that will be removed. Remove the `index.` - // from the name. - if pkgName[0:17] == "index.unikraft.io" { - pkgName = pkgName[6:] - } - if pkgName[0:12] == "unikraft.io/" { - pkgName = pkgName[12:] + if len(instances) == 1 && opts.Output == "" { + utils.PrettyPrintInstance(ctx, &instances[0], !opts.NoStart) + return nil } - var instance *kraftcloudinstances.Instance - - paramodel, err := processtree.NewProcessTree( - ctx, - []processtree.ProcessTreeOption{ - processtree.IsParallel(false), - processtree.WithRenderer( - log.LoggerTypeFromString(config.G[config.KraftKit](ctx).Log.Type) != log.FANCY, - ), - processtree.WithFailFast(true), - processtree.WithHideOnSuccess(true), - processtree.WithTimeout(opts.Timeout), - }, - processtree.NewProcessTreeItem( - "deploying", - "", - func(ctx context.Context) error { - checkRemoteImages: - for { - // First check if the context has been cancelled - select { - case <-ctx.Done(): - return fmt.Errorf("context cancelled") - default: - } - - ctxTimeout, cancel := context.WithTimeout(ctx, 30*time.Second) - defer cancel() - - images, err := opts.Client.Images().WithMetro(opts.Metro).List(ctxTimeout) - if err != nil { - return fmt.Errorf("could not check list of images: %w", err) - } - - for _, image := range images { - split := strings.Split(image.Digest, "@sha256:") - if !strings.HasPrefix(split[len(split)-1], digest) { - continue - } - - break checkRemoteImages - } - } - - instance, err = create.Create(ctx, &create.CreateOptions{ - Env: opts.Env, - FQDN: opts.FQDN, - Memory: opts.Memory, - Metro: opts.Metro, - Name: opts.Name, - Ports: opts.Ports, - Replicas: opts.Replicas, - Start: !opts.NoStart, - SubDomain: opts.SubDomain, - }, pkgName) - if err != nil { - return fmt.Errorf("could not create instance: %w", err) - } - - return nil - }, - ), - ) - if err != nil { - return err - } - - if err := paramodel.Start(); err != nil { - return err - } - - utils.PrettyPrintInstance(ctx, instance, !opts.NoStart) - - return nil + return utils.PrintInstances(ctx, opts.Output, instances...) } diff --git a/internal/cli/kraft/cloud/deploy/deployer.go b/internal/cli/kraft/cloud/deploy/deployer.go index ac3cbe2a8..c0be10948 100644 --- a/internal/cli/kraft/cloud/deploy/deployer.go +++ b/internal/cli/kraft/cloud/deploy/deployer.go @@ -9,7 +9,7 @@ import ( "context" "fmt" - "kraftkit.sh/pack" + kraftcloudinstances "sdk.kraft.cloud/instances" ) // deployer is an interface for defining different mechanisms to perform a the @@ -25,8 +25,8 @@ type deployer interface { // current implementation. Deployable(context.Context, *DeployOptions, ...string) (bool, error) - // Prepare performs the deployment based on the determined implementation. - Prepare(context.Context, *DeployOptions, ...string) ([]pack.Package, error) + // Deploy performs the deployment based on the determined implementation. + Deploy(context.Context, *DeployOptions, ...string) ([]kraftcloudinstances.Instance, error) } // deployers is the list of built-in deployers which are checked diff --git a/internal/cli/kraft/cloud/deploy/deployer_kraftfile_runtime.go b/internal/cli/kraft/cloud/deploy/deployer_kraftfile_runtime.go index b7dfb6ecf..69c0a5eda 100644 --- a/internal/cli/kraft/cloud/deploy/deployer_kraftfile_runtime.go +++ b/internal/cli/kraft/cloud/deploy/deployer_kraftfile_runtime.go @@ -8,10 +8,17 @@ package deploy import ( "context" "fmt" + "path/filepath" "strings" + "time" + "kraftkit.sh/config" + "kraftkit.sh/internal/cli/kraft/cloud/instance/create" "kraftkit.sh/internal/cli/kraft/pkg" - "kraftkit.sh/pack" + "kraftkit.sh/log" + "kraftkit.sh/tui/processtree" + + kraftcloudinstances "sdk.kraft.cloud/instances" ) type deployerKraftfileRuntime struct{} @@ -48,16 +55,130 @@ func (deployer *deployerKraftfileRuntime) Deployable(ctx context.Context, opts * return true, nil } -func (deployer *deployerKraftfileRuntime) Prepare(ctx context.Context, opts *DeployOptions, args ...string) ([]pack.Package, error) { - return pkg.Pkg(ctx, &pkg.PkgOptions{ +func (deployer *deployerKraftfileRuntime) Deploy(ctx context.Context, opts *DeployOptions, args ...string) ([]kraftcloudinstances.Instance, error) { + var pkgName string + + if len(opts.Name) > 0 { + pkgName = opts.Name + } else if opts.Project != nil && len(opts.Project.Name()) > 0 { + pkgName = opts.Project.Name() + } else { + pkgName = filepath.Base(opts.Workdir) + } + + if strings.HasPrefix(pkgName, "unikraft.io") { + pkgName = "index." + pkgName + } + if !strings.HasPrefix(pkgName, "index.unikraft.io") { + pkgName = fmt.Sprintf( + "index.unikraft.io/%s/%s:latest", + strings.TrimSuffix(strings.TrimPrefix(opts.Auth.User, "robot$"), ".users.kraftcloud"), + pkgName, + ) + } + + packs, err := pkg.Pkg(ctx, &pkg.PkgOptions{ Architecture: "x86_64", Format: "oci", Kraftfile: opts.Kraftfile, - Name: args[0], + Name: pkgName, Platform: "kraftcloud", Project: opts.Project, Push: true, Strategy: opts.Strategy, Workdir: opts.Workdir, }) + + // TODO(nderjung): This is a quirk that will be removed. Remove the `index.` + // from the name. + if pkgName[0:17] == "index.unikraft.io" { + pkgName = pkgName[6:] + } + if pkgName[0:12] == "unikraft.io/" { + pkgName = pkgName[12:] + } + + // FIXME(nderjung): Gathering the digest like this really dirty. + metadata := packs[0].Columns() + var digest string + for _, m := range metadata { + if m.Name != "index" { + continue + } + + digest = m.Value + } + + var instance *kraftcloudinstances.Instance + + paramodel, err := processtree.NewProcessTree( + ctx, + []processtree.ProcessTreeOption{ + processtree.IsParallel(false), + processtree.WithRenderer( + log.LoggerTypeFromString(config.G[config.KraftKit](ctx).Log.Type) != log.FANCY, + ), + processtree.WithFailFast(true), + processtree.WithHideOnSuccess(true), + processtree.WithTimeout(opts.Timeout), + }, + processtree.NewProcessTreeItem( + "deploying", + "", + func(ctx context.Context) error { + checkRemoteImages: + for { + // First check if the context has been cancelled + select { + case <-ctx.Done(): + return fmt.Errorf("context cancelled") + default: + } + + ctxTimeout, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + + images, err := opts.Client.Images().WithMetro(opts.Metro).List(ctxTimeout) + if err != nil { + return fmt.Errorf("could not check list of images: %w", err) + } + + for _, image := range images { + split := strings.Split(image.Digest, "@sha256:") + if !strings.HasPrefix(split[len(split)-1], digest) { + continue + } + + break checkRemoteImages + } + } + + instance, err = create.Create(ctx, &create.CreateOptions{ + Env: opts.Env, + FQDN: opts.FQDN, + Memory: opts.Memory, + Metro: opts.Metro, + Name: opts.Name, + Ports: opts.Ports, + Replicas: opts.Replicas, + Start: !opts.NoStart, + SubDomain: opts.SubDomain, + }, pkgName) + if err != nil { + return fmt.Errorf("could not create instance: %w", err) + } + + return nil + }, + ), + ) + if err != nil { + return nil, err + } + + if err := paramodel.Start(); err != nil { + return nil, err + } + + return []kraftcloudinstances.Instance{*instance}, nil } From 94e3ac740275ba7a6da9c8406512777f71c85147 Mon Sep 17 00:00:00 2001 From: Alexander Jung Date: Fri, 8 Dec 2023 17:34:46 +0100 Subject: [PATCH 3/5] feat(deploy): Introduce deployer which handles full Unikraft builds This deployer handles a context where the user wishes to construct the kernel, and as a result the `unikraft` attribute of the `Kraftfile` is present. Re-use the build method and options from the `kraft build` sub command as well as the runtime packager which will understand a context that has a built unikernel. Signed-off-by: Alexander Jung --- internal/cli/kraft/cloud/deploy/deploy.go | 46 +++++++++------ internal/cli/kraft/cloud/deploy/deployer.go | 1 + .../deploy/deployer_kraftfile_unikraft.go | 58 +++++++++++++++++++ 3 files changed, 87 insertions(+), 18 deletions(-) create mode 100644 internal/cli/kraft/cloud/deploy/deployer_kraftfile_unikraft.go diff --git a/internal/cli/kraft/cloud/deploy/deploy.go b/internal/cli/kraft/cloud/deploy/deploy.go index a48cf0c5a..1f264fe5f 100644 --- a/internal/cli/kraft/cloud/deploy/deploy.go +++ b/internal/cli/kraft/cloud/deploy/deploy.go @@ -26,24 +26,34 @@ import ( ) type DeployOptions struct { - Auth *config.AuthConfig `noattribute:"true"` - Client kraftcloud.KraftCloud `noattribute:"true"` - Env []string `local:"true" long:"env" short:"e" usage:"Environmental variables"` - FQDN string `local:"true" long:"fqdn" short:"d" usage:"Set the fully qualified domain name for the service"` - Kraftfile string `local:"true" long:"kraftfile" short:"K" usage:"Set the Kraftfile to use"` - Memory int64 `local:"true" long:"memory" short:"M" usage:"Specify the amount of memory to allocate"` - Metro string `noattribute:"true"` - Name string `local:"true" long:"name" short:"n" usage:"Name of the deployment"` - NoStart bool `local:"true" long:"no-start" short:"S" usage:"Do not start the instance after creation"` - Output string `local:"true" long:"output" short:"o" usage:"Set output format"` - Ports []string `local:"true" long:"port" short:"p" usage:"Specify the port mapping between external to internal"` - Project app.Application `noattribute:"true"` - Replicas int `local:"true" long:"replicas" short:"R" usage:"Number of replicas of the instance" default:"0"` - Rootfs string `local:"true" long:"rootfs" usage:"Specify a path to use as root filesystem"` - Strategy packmanager.MergeStrategy `noattribute:"true"` - SubDomain string `local:"true" long:"subdomain" short:"s" usage:"Set the name to use when provisioning a subdomain"` - Timeout time.Duration `local:"true" long:"timeout" usage:"Set the timeout for remote procedure calls"` - Workdir string `local:"true" long:"workdir" short:"w" usage:"Set an alternative working directory (default is cwd)"` + Auth *config.AuthConfig `noattribute:"true"` + Client kraftcloud.KraftCloud `noattribute:"true"` + DotConfig string `long:"config" short:"c" usage:"Override the path to the KConfig .config file"` + Env []string `local:"true" long:"env" short:"e" usage:"Environmental variables"` + ForcePull bool `long:"force-pull" usage:"Force pulling packages before building"` + FQDN string `local:"true" long:"fqdn" short:"d" usage:"Set the fully qualified domain name for the service"` + Jobs int `long:"jobs" short:"j" usage:"Allow N jobs at once"` + KernelDbg bool `long:"dbg" usage:"Build the debuggable (symbolic) kernel image instead of the stripped image"` + Kraftfile string `local:"true" long:"kraftfile" short:"K" usage:"Set the Kraftfile to use"` + Memory int64 `local:"true" long:"memory" short:"M" usage:"Specify the amount of memory to allocate"` + Metro string `noattribute:"true"` + Name string `local:"true" long:"name" short:"n" usage:"Name of the deployment"` + NoCache bool `long:"no-cache" short:"F" usage:"Force a rebuild even if existing intermediate artifacts already exist"` + NoConfigure bool `long:"no-configure" usage:"Do not run Unikraft's configure step before building"` + NoFast bool `long:"no-fast" usage:"Do not use maximum parallelization when performing the build"` + NoFetch bool `long:"no-fetch" usage:"Do not run Unikraft's fetch step before building"` + NoStart bool `local:"true" long:"no-start" short:"S" usage:"Do not start the instance after creation"` + NoUpdate bool `long:"no-update" usage:"Do not update package index before running the build"` + Output string `local:"true" long:"output" short:"o" usage:"Set output format"` + Ports []string `local:"true" long:"port" short:"p" usage:"Specify the port mapping between external to internal"` + Project app.Application `noattribute:"true"` + Replicas int `local:"true" long:"replicas" short:"R" usage:"Number of replicas of the instance" default:"0"` + Rootfs string `local:"true" long:"rootfs" usage:"Specify a path to use as root filesystem"` + SaveBuildLog string `long:"build-log" usage:"Use the specified file to save the output from the build"` + Strategy packmanager.MergeStrategy `noattribute:"true"` + SubDomain string `local:"true" long:"subdomain" short:"s" usage:"Set the name to use when provisioning a subdomain"` + Timeout time.Duration `local:"true" long:"timeout" usage:"Set the timeout for remote procedure calls"` + Workdir string `local:"true" long:"workdir" short:"w" usage:"Set an alternative working directory (default is cwd)"` } func NewCmd() *cobra.Command { diff --git a/internal/cli/kraft/cloud/deploy/deployer.go b/internal/cli/kraft/cloud/deploy/deployer.go index c0be10948..5ea4ddefe 100644 --- a/internal/cli/kraft/cloud/deploy/deployer.go +++ b/internal/cli/kraft/cloud/deploy/deployer.go @@ -35,5 +35,6 @@ type deployer interface { func deployers() []deployer { return []deployer{ &deployerKraftfileRuntime{}, + &deployerKraftfileUnikraft{}, } } diff --git a/internal/cli/kraft/cloud/deploy/deployer_kraftfile_unikraft.go b/internal/cli/kraft/cloud/deploy/deployer_kraftfile_unikraft.go new file mode 100644 index 000000000..bb5ad747d --- /dev/null +++ b/internal/cli/kraft/cloud/deploy/deployer_kraftfile_unikraft.go @@ -0,0 +1,58 @@ +package deploy + +import ( + "context" + "fmt" + + "kraftkit.sh/internal/cli/kraft/build" + kraftcloudinstances "sdk.kraft.cloud/instances" +) + +type deployerKraftfileUnikraft struct{} + +func (deployer *deployerKraftfileUnikraft) String() string { + return "kraftfile-runtime" +} + +func (deployer *deployerKraftfileUnikraft) Deployable(ctx context.Context, opts *DeployOptions, args ...string) (bool, error) { + if opts.Project == nil { + if err := opts.initProject(ctx); err != nil { + return false, err + } + } + + if opts.Project.Runtime() != nil { + return false, nil + } + + if opts.Project.Unikraft(ctx) == nil { + return false, fmt.Errorf("cannot package without unikraft attribute") + } + + return true, nil +} + +func (deployer *deployerKraftfileUnikraft) Deploy(ctx context.Context, opts *DeployOptions, args ...string) ([]kraftcloudinstances.Instance, error) { + if err := build.Build(ctx, &build.BuildOptions{ + Architecture: "x86_64", + DotConfig: opts.DotConfig, + ForcePull: opts.ForcePull, + Jobs: opts.Jobs, + KernelDbg: opts.KernelDbg, + NoCache: opts.NoCache, + NoConfigure: opts.NoConfigure, + NoFast: opts.NoFast, + NoFetch: opts.NoFetch, + NoUpdate: opts.NoUpdate, + Platform: "kraftcloud", + Rootfs: opts.Rootfs, + SaveBuildLog: opts.SaveBuildLog, + Workdir: opts.Workdir, + }); err != nil { + return nil, fmt.Errorf("could not complete build") + } + + // Re-use the runtime deployer, which also handles packaging. + runtimeDeployer := &deployerKraftfileRuntime{} + return runtimeDeployer.Deploy(ctx, opts, args...) +} From 3173feefc24c5c532c9c7b7d8ea90027323e5db1 Mon Sep 17 00:00:00 2001 From: Alexander Jung Date: Fri, 8 Dec 2023 17:50:56 +0100 Subject: [PATCH 4/5] feat(deploy): Introduce alias for instance create This new deployer is essential an alias for ``` kraft cloud instance create IMAGE ``` And allows the short-hand syntax: ``` kraft cloud deploy IMAGE ``` Signed-off-by: Alexander Jung --- internal/cli/kraft/cloud/deploy/deploy.go | 7 +- internal/cli/kraft/cloud/deploy/deployer.go | 1 + .../kraft/cloud/deploy/deployer_image_name.go | 80 +++++++++++++++++++ 3 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 internal/cli/kraft/cloud/deploy/deployer_image_name.go diff --git a/internal/cli/kraft/cloud/deploy/deploy.go b/internal/cli/kraft/cloud/deploy/deploy.go index 1f264fe5f..1477632a2 100644 --- a/internal/cli/kraft/cloud/deploy/deploy.go +++ b/internal/cli/kraft/cloud/deploy/deploy.go @@ -66,8 +66,11 @@ func NewCmd() *cobra.Command { }, Example: heredoc.Docf(` # Create a new deployment at https://hello-world.fra0.kraft.cloud in Frankfurt - # of your current working directory which exposes a port at 8080: - kraft cloud --metro fra0 deploy --subdomain hello-world -p 443:8080 .`), + # of your current working directory which exposes a port at 8080. + kraft cloud --metro fra0 deploy --subdomain hello-world -p 443:8080 . + + # Alternatively supply an existing image which is available in the catalog: + kraft cloud --metro fra0 deploy -p 443:8080 caddy:latest`), }) if err != nil { panic(err) diff --git a/internal/cli/kraft/cloud/deploy/deployer.go b/internal/cli/kraft/cloud/deploy/deployer.go index 5ea4ddefe..b8f057c8e 100644 --- a/internal/cli/kraft/cloud/deploy/deployer.go +++ b/internal/cli/kraft/cloud/deploy/deployer.go @@ -34,6 +34,7 @@ type deployer interface { // is used with the controller. func deployers() []deployer { return []deployer{ + &deployerImageName{}, &deployerKraftfileRuntime{}, &deployerKraftfileUnikraft{}, } diff --git a/internal/cli/kraft/cloud/deploy/deployer_image_name.go b/internal/cli/kraft/cloud/deploy/deployer_image_name.go new file mode 100644 index 000000000..0f7b84b58 --- /dev/null +++ b/internal/cli/kraft/cloud/deploy/deployer_image_name.go @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2023, Unikraft GmbH and The KraftKit Authors. +// Licensed under the BSD-3-Clause License (the "License"). +// You may not use this file except in compliance with the License. + +package deploy + +import ( + "context" + "fmt" + + "kraftkit.sh/config" + "kraftkit.sh/internal/cli/kraft/cloud/instance/create" + "kraftkit.sh/log" + "kraftkit.sh/tui/processtree" + kraftcloudinstances "sdk.kraft.cloud/instances" +) + +type deployerImageName struct{} + +func (deployer *deployerImageName) String() string { + return "image-name" +} + +func (deployer *deployerImageName) Deployable(ctx context.Context, opts *DeployOptions, args ...string) (bool, error) { + if err := opts.initProject(ctx); err != nil && len(args) > 0 { + return true, nil + } + + return false, fmt.Errorf("context contains project") +} + +func (deployer *deployerImageName) Deploy(ctx context.Context, opts *DeployOptions, args ...string) ([]kraftcloudinstances.Instance, error) { + var err error + var instance *kraftcloudinstances.Instance + + paramodel, err := processtree.NewProcessTree( + ctx, + []processtree.ProcessTreeOption{ + processtree.IsParallel(false), + processtree.WithRenderer( + log.LoggerTypeFromString(config.G[config.KraftKit](ctx).Log.Type) != log.FANCY, + ), + processtree.WithFailFast(true), + processtree.WithHideOnSuccess(true), + processtree.WithTimeout(opts.Timeout), + }, + processtree.NewProcessTreeItem( + "deploying", + "", + func(ctx context.Context) error { + instance, err = create.Create(ctx, &create.CreateOptions{ + Env: opts.Env, + FQDN: opts.FQDN, + Memory: opts.Memory, + Metro: opts.Metro, + Name: opts.Name, + Ports: opts.Ports, + Replicas: opts.Replicas, + Start: !opts.NoStart, + SubDomain: opts.SubDomain, + }, args[0]) + if err != nil { + return fmt.Errorf("could not create instance: %w", err) + } + + return nil + }, + ), + ) + if err != nil { + return nil, err + } + + if err := paramodel.Start(); err != nil { + return nil, err + } + + return []kraftcloudinstances.Instance{*instance}, nil +} From 1cd834fc757104692f19e465342542c4cce8287e Mon Sep 17 00:00:00 2001 From: Alexander Jung Date: Fri, 8 Dec 2023 17:58:46 +0100 Subject: [PATCH 5/5] fix(deploy): Fix unhandled error Signed-off-by: Alexander Jung --- internal/cli/kraft/cloud/deploy/deployer_kraftfile_runtime.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/cli/kraft/cloud/deploy/deployer_kraftfile_runtime.go b/internal/cli/kraft/cloud/deploy/deployer_kraftfile_runtime.go index 69c0a5eda..53412350f 100644 --- a/internal/cli/kraft/cloud/deploy/deployer_kraftfile_runtime.go +++ b/internal/cli/kraft/cloud/deploy/deployer_kraftfile_runtime.go @@ -88,6 +88,9 @@ func (deployer *deployerKraftfileRuntime) Deploy(ctx context.Context, opts *Depl Strategy: opts.Strategy, Workdir: opts.Workdir, }) + if err != nil { + return nil, fmt.Errorf("could not package: %w", err) + } // TODO(nderjung): This is a quirk that will be removed. Remove the `index.` // from the name.