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

Process and display build and push logs #450

Merged
merged 15 commits into from
Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from 12 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
1 change: 0 additions & 1 deletion examples/examples_dotnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ func TestNginxCs(t *testing.T) {
}

func TestDotNet(t *testing.T) {

t0yv0 marked this conversation as resolved.
Show resolved Hide resolved
test := getCsharpBaseOptions(t).
With(integration.ProgramTestOptions{
Dir: path.Join(getCwd(t), "dotnet"),
Expand Down
4 changes: 3 additions & 1 deletion provider/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/pulumi/pulumi-docker/provider/v4
go 1.19

require (
github.com/docker/cli v20.10.21+incompatible
github.com/docker/docker v20.10.21+incompatible
github.com/golang/protobuf v1.5.2
github.com/moby/buildkit v0.10.5
Expand All @@ -11,6 +12,7 @@ require (
github.com/pulumi/pulumi-terraform-bridge/v3 v3.33.0
github.com/pulumi/pulumi/pkg/v3 v3.44.2
github.com/pulumi/pulumi/sdk/v3 v3.44.2
github.com/ryboe/q v1.0.18
github.com/stretchr/testify v1.8.0
github.com/terraform-providers/terraform-provider-docker/shim v0.0.0
google.golang.org/grpc v1.49.0
Expand Down Expand Up @@ -92,7 +94,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/djherbis/times v1.5.0 // indirect
github.com/docker/cli v20.10.21+incompatible // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.6.4 // indirect
github.com/docker/go-connections v0.4.0 // indirect
Expand Down Expand Up @@ -166,6 +167,7 @@ require (
github.com/kevinburke/ssh_config v1.1.0 // indirect
github.com/klauspost/compress v1.15.1 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
Expand Down
2 changes: 2 additions & 0 deletions provider/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1615,6 +1615,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/ryboe/q v1.0.18 h1:uTonPt1eZjy7GSpB0XpYpsCvX+Yf9f+M4CUKuH2r+vg=
github.com/ryboe/q v1.0.18/go.mod h1:elqvVf/GBuZHvZ9gvHv4MKM6NZAMz2rFajnTgQZ46wU=
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
Expand Down
126 changes: 96 additions & 30 deletions provider/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/moby/buildkit/session"
"net"

"github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/credentials"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/archive"
Expand All @@ -14,6 +19,7 @@ import (
"github.com/pkg/errors"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
"github.com/ryboe/q"
t0yv0 marked this conversation as resolved.
Show resolved Hide resolved
)

const defaultDockerfile = "Dockerfile"
Expand Down Expand Up @@ -77,7 +83,7 @@ func (p *dockerNativeProvider) dockerBuild(ctx context.Context,
return "", nil, err
}

err = p.host.Log(ctx, "info", urn, "Building the image")
err = p.host.LogStatus(ctx, "info", urn, "Building the image")

if err != nil {
return "", nil, err
Expand All @@ -89,15 +95,50 @@ func (p *dockerNativeProvider) dockerBuild(ctx context.Context,
return "", nil, err
}

// make the build options
// get credentials
t0yv0 marked this conversation as resolved.
Show resolved Hide resolved
creds, err := config.Load(config.Dir())
if err != nil {
return "", props, err
}
creds.CredentialsStore = credentials.DetectDefaultStore(creds.CredentialsStore)
auths, err := creds.GetAllCredentials()
if err != nil {
return "", nil, err
}
// read auths to a map of authConfigs for the build options to consume
authConfigs := make(map[string]types.AuthConfig, len(auths))
for k, auth := range auths {
authConfigs[k] = types.AuthConfig(auth)
}

//sess, _ := session.NewSession(ctx, "pulumi-docker", "")
t0yv0 marked this conversation as resolved.
Show resolved Hide resolved
//dialSession := func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) {
// return docker.DialHijack(ctx, "/session", proto, meta)
//}
//go sess.Run(ctx, dialSession)
//defer sess.Close()

// make the build options
opts := types.ImageBuildOptions{
Dockerfile: img.Build.Dockerfile,
Tags: []string{img.Name}, //this should build the image locally, sans registry info
Remove: true,
//CacheFrom: img.Build.CachedImages, // TODO: this needs a login, so needs to be handled differently.
BuildArgs: build.Args,
Version: build.BuilderVersion,

AuthConfigs: authConfigs,
}

// Start a session for BuildKit
if build.BuilderVersion == defaultBuilder {
sess, _ := session.NewSession(ctx, "pulumi-docker", "")
dialSession := func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) {
return docker.DialHijack(ctx, "/session", proto, meta)
}
go sess.Run(ctx, dialSession)
t0yv0 marked this conversation as resolved.
Show resolved Hide resolved
defer sess.Close()
opts.SessionID = sess.ID()
}

imgBuildResp, err := docker.ImageBuild(ctx, tar, opts)
Expand All @@ -106,10 +147,16 @@ func (p *dockerNativeProvider) dockerBuild(ctx context.Context,
}

defer imgBuildResp.Body.Close()
// Print build logs to terminal

// Print build logs to `Info` progress report
scanner := bufio.NewScanner(imgBuildResp.Body)
for scanner.Scan() {
err := p.host.Log(ctx, "info", urn, scanner.Text())

info, err := processLogLine(scanner.Text())
if err != nil {
return "", nil, err
}
err = p.host.Log(ctx, "info", urn, info)
if err != nil {
return "", nil, err
}
Expand All @@ -130,13 +177,13 @@ func (p *dockerNativeProvider) dockerBuild(ctx context.Context,
return img.Name, pbstruct, err
}

err = p.host.Log(ctx, "info", urn, "Pushing Image to the registry")
err = p.host.LogStatus(ctx, "info", urn, "Pushing Image to the registry")

if err != nil {
return "", nil, err
}
// Quick and dirty auth; we can also preconfigure the client itself I believe

// TODO: use auth pattern as above to use default auth
var authConfig = types.AuthConfig{
Username: img.Registry.Username,
Password: img.Registry.Password,
Expand All @@ -161,35 +208,17 @@ func (p *dockerNativeProvider) dockerBuild(ctx context.Context,

defer pushOutput.Close()

// Print push logs to terminal
// Print push logs to `Info` progress report
pushScanner := bufio.NewScanner(pushOutput)
for pushScanner.Scan() {
msg := pushScanner.Text()
var jsmsg jsonmessage.JSONMessage
err := json.Unmarshal([]byte(msg), &jsmsg)
info, err := processLogLine(pushScanner.Text())
if err != nil {
return "", nil, errors.Wrapf(err, "encountered error unmarshalling:")
}
if jsmsg.Status != "" {
if jsmsg.Status != "Pushing" {
var info string
if jsmsg.ID != "" {
info = fmt.Sprintf("%s: %s", jsmsg.ID, jsmsg.Status)
} else {
info = jsmsg.Status

}
err := p.host.Log(ctx, "info", urn, info)
if err != nil {
return "", nil, err
}
}
return "", nil, err
}

if jsmsg.Error != nil {
return "", nil, errors.Errorf(jsmsg.Error.Message)
err = p.host.LogStatus(ctx, "info", urn, info)
if err != nil {
return "", nil, err
}

}

outputs := map[string]interface{}{
Expand Down Expand Up @@ -370,3 +399,40 @@ func marshalSkipPush(sp resource.PropertyValue) bool {
}
return sp.BoolValue()
}

func processLogLine(msg string) (string, error) {
var info string
var jm jsonmessage.JSONMessage
err := json.Unmarshal([]byte(msg), &jm)
fmt.Println("HERE IT IS", jm)
t0yv0 marked this conversation as resolved.
Show resolved Hide resolved
q.Q(jm)
if err != nil {
return info, errors.Wrapf(err, "encountered error unmarshalling:")
}
// process this JSONMessage
if jm.Error != nil {
if jm.Error.Code == 401 {
return info, fmt.Errorf("authentication is required")
}
return info, errors.Errorf(jm.Error.Message)
}
if jm.ID != "" {
info += jm.ID
}
if jm.From != "" {
info += jm.From
}
if jm.Progress != nil {
info = jm.Status + " " + jm.Progress.String()
} else if jm.Stream != "" {
info += jm.Stream
} else {
info += jm.Status
}
if jm.Aux != nil {
infoBytes, _ := json.Marshal(jm.Aux)
// because this is unstructured JSON we print out the whole object.
info += string(infoBytes) //TODO:
t0yv0 marked this conversation as resolved.
Show resolved Hide resolved
}
return info, nil
}