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

appdev: pull required container images #1240

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
94 changes: 0 additions & 94 deletions cmd/charm-test/main.go

This file was deleted.

144 changes: 122 additions & 22 deletions commands/apps_dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package commands

import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
Expand All @@ -23,6 +24,8 @@ import (
"github.com/digitalocean/doctl/internal/apps/builder"
"github.com/digitalocean/doctl/internal/apps/config"
"github.com/digitalocean/doctl/internal/apps/workspace"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/jsonmessage"

"github.com/digitalocean/godo"
"github.com/spf13/cobra"
Expand All @@ -39,8 +42,9 @@ func AppsDev() *Command {
Command: &cobra.Command{
Use: "dev",
Aliases: []string{},
Short: "Display commands for working with app platform local development.",
Long: `Display commands for working with app platform local development.`,
Short: "[BETA] Display commands for working with app platform local development.",
Long: `[BETA] Display commands for working with app platform local development.`,
Hidden: true,
},
}

Expand All @@ -52,7 +56,7 @@ func AppsDev() *Command {
"build [component name]",
"Build an app component",
heredoc.Doc(`
Build an app component locally.
[BETA] Build an app component locally.

The component name must be specified as an argument if running non-interactively.`,
),
Expand Down Expand Up @@ -125,7 +129,7 @@ func RunAppsDevBuild(c *CmdConfig) error {
// link an existing app.
if ws.Config.AppSpec == nil {
// TODO(ntate); allow app-detect build to remove requirement
return errors.New("app spec is required for component build")
return errors.New("please place an app spec at .do/app.yaml or link an existing app using the --app flag")
}

var hasBuildableComponents bool
Expand Down Expand Up @@ -198,6 +202,25 @@ func RunAppsDevBuild(c *CmdConfig) error {
}
}

cli, err := c.Doit.GetDockerEngineClient()
if err != nil {
return err
}

err = appDevPrepareEnvironment(ctx, ws, cli, componentSpec)
if err != nil {
_, isSnap := os.LookupEnv("SNAP")
if isSnap && errors.Is(err, fs.ErrPermission) {
template.Buffered(
textbox.New().Warning(),
`Using the doctl Snap? Grant doctl access to Docker by running {{highlight "sudo snap connect doctl:app-dev-build docker:docker-daemon"}}`,
nil,
)
}

return fmt.Errorf("preparing build environment: %w", err)
}

if component.EnvFile != "" {
template.Print(`{{success checkmark}} using envs from {{highlight .}}{{nl}}`, component.EnvFile)
} else if Interactive && fileExists(ws.Context(AppsDevDefaultEnvFile)) {
Expand Down Expand Up @@ -243,29 +266,21 @@ func RunAppsDevBuild(c *CmdConfig) error {
// }
// }

cli, err := c.Doit.GetDockerEngineClient()
if err != nil {
return err
}

ctx, cancel := context.WithCancel(ctx)
defer cancel()

buildingComponentLine := template.String(
`building {{lower (snakeToTitle .GetType)}} {{highlight .GetName}}`,
componentSpec,
)
template.Print(`{{success checkmark}} {{.}}{{nl 2}}`, buildingComponentLine)

// TODO intercept ctrl-c and allow for graceful shutdown & container cleanup

var (
wg sync.WaitGroup
logWriter io.Writer

// userCanceled indicates whether the context was canceled by user request
userCanceled bool
)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if Interactive {
logPager, err := pager.New(
pager.WithTitle(buildingComponentLine),
Expand Down Expand Up @@ -318,14 +333,15 @@ func RunAppsDevBuild(c *CmdConfig) error {
defer cancel()

builder, err := c.componentBuilderFactory.NewComponentBuilder(cli, ws.Context(), ws.Config.AppSpec, builder.NewBuilderOpts{
Component: componentName,
LocalCacheDir: ws.CacheDir(componentName),
NoCache: ws.Config.NoCache,
Registry: ws.Config.Registry,
EnvOverride: component.Envs,
BuildCommandOverride: component.BuildCommand,
LogWriter: logWriter,
Versioning: builder.Versioning{CNB: ws.Config.App.GetBuildConfig().GetCNBVersioning()},
Component: componentName,
LocalCacheDir: ws.CacheDir(componentName),
NoCache: ws.Config.NoCache,
Registry: ws.Config.Registry,
EnvOverride: component.Envs,
BuildCommandOverride: component.BuildCommand,
CNBBuilderImageOverride: ws.Config.CNBBuilderImage,
LogWriter: logWriter,
Versioning: builder.Versioning{CNB: ws.Config.App.GetBuildConfig().GetCNBVersioning()},
})
if err != nil {
return err
Expand Down Expand Up @@ -397,3 +413,87 @@ func appDevWorkspace(cmdConfig *CmdConfig) (*workspace.AppDev, error) {
AppsService: cmdConfig.Apps(),
})
}

// PrepareEnvironment pulls required images, validates permissions, etc. in preparation for a component build.
func appDevPrepareEnvironment(ctx context.Context, ws *workspace.AppDev, cli builder.DockerEngineClient, componentSpec godo.AppBuildableComponentSpec) error {
var images []string
if componentSpec.GetDockerfilePath() == "" {
// CNB build
if ws.Config.CNBBuilderImage != "" {
images = append(images, ws.Config.CNBBuilderImage)
} else {
images = append(images, builder.CNBBuilderImage)
}

// TODO: get stack run image from builder image md after we pull it, see below
images = append(images, "digitaloceanapps/apps-run:7858f2c")
}

if componentSpec.GetType() == godo.AppComponentTypeStaticSite {
images = append(images, builder.StaticSiteNginxImage)
}

var toPull []string
for _, ref := range images {
exists, err := builder.ImageExists(ctx, cli, ref)
if err != nil {
return err
}
if !exists {
toPull = append(toPull, ref)
}
// TODO pull if image might be stale
}

err := pullDockerImages(ctx, cli, toPull)
if err != nil {
return err
}

// TODO: get stack run image from builder image md
// builderImage, err := builder.GetImage(ctx, cli, cnbBuilderImage)
// if err != nil {
// return err
// }
// builderImage.Labels["io.buildpacks.builder.metadata"]

return nil
}

func pullDockerImages(ctx context.Context, cli builder.DockerEngineClient, images []string) error {
for _, ref := range images {
template.Print(`{{success checkmark}} pulling container image {{highlight .}}{{nl}}`, ref)

r, err := cli.ImagePull(ctx, ref, types.ImagePullOptions{})
if err != nil {
return fmt.Errorf("pulling container image %s: %w", ref, err)
}
defer r.Close()

dec := json.NewDecoder(r)
for {
var jm jsonmessage.JSONMessage
err := dec.Decode(&jm)
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return err
}

if jm.Error != nil {
return jm.Error
}

if jm.Aux != nil {
continue
}

if jm.Progress != nil {
fmt.Printf("\r%s", charm.IndentString(2, text.Muted.S(jm.Progress.String()))) // go back to the start of the line and print the bar
}
}
fmt.Printf("\r%s\n", charm.IndentString(2, template.String(`{{success checkmark}} done`, nil)))
}
return nil
}
10 changes: 1 addition & 9 deletions commands/apps_dev_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func AppsDevConfig() *Command {
Use: "config",
Aliases: []string{"c"},
Short: "Display commands for working with app platform local development configuration files.",
Long: `Display commands for working with app platform local development configuration files.`,
Long: `[BETA] Display commands for working with app platform local development configuration files.`,
},
}

Expand All @@ -34,10 +34,6 @@ func AppsDevConfig() *Command {
"set KEY=VALUE...",
"Set dev configuration settings.",
"Set dev configuration settings for a build.",
// fmt.Sprintf(`Set dev configuration settings for a build.

// Valid Keys: %s
// `, config.ValidAppDevKeys()),
Writer,
)

Expand All @@ -53,10 +49,6 @@ func AppsDevConfig() *Command {
"unset KEY...",
"Unset dev configuration settings.",
"Unset dev configuration settings for a build.",
// fmt.Sprintf(`Unset dev configuration settings for a build.

// Valid Keys: %s
// `, config.ValidAppDevKeys()),
Writer,
)

Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ require (
)

require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/containerd/cgroups v1.0.1 // indirect
Expand Down Expand Up @@ -86,8 +87,10 @@ require (
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/moby/sys/mount v0.3.3 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect
github.com/muesli/cancelreader v0.2.1 // indirect
github.com/muesli/termenv v0.12.0 // indirect
Expand Down
Loading