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

Set Registry Server Name #465

Merged
merged 10 commits into from
Jan 27, 2023
2 changes: 1 addition & 1 deletion provider/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.19

require (
github.com/docker/cli v20.10.21+incompatible
github.com/docker/distribution v2.8.1+incompatible
github.com/docker/docker v20.10.21+incompatible
github.com/golang/protobuf v1.5.2
github.com/moby/buildkit v0.10.5
Expand Down Expand Up @@ -95,7 +96,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/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
github.com/docker/go-units v0.5.0 // indirect
Expand Down
57 changes: 39 additions & 18 deletions provider/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/docker/distribution/reference"
"github.com/moby/buildkit/session"
"net"
"strings"
Expand All @@ -19,7 +20,6 @@ import (
"github.com/docker/docker/pkg/jsonmessage"
structpb "github.com/golang/protobuf/ptypes/struct"
controlapi "github.com/moby/buildkit/api/services/control"
"github.com/pkg/errors"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
)
Expand Down Expand Up @@ -177,13 +177,14 @@ func (p *dockerNativeProvider) dockerBuild(ctx context.Context,
}

// authentication for registry push
// we check if the user set creds in the Pulumi program, and use those preferentially
// we check if the user set creds in the Pulumi program, and use those preferentially,
// otherwise we use host machine creds via authConfigs.
var pushAuthConfig types.AuthConfig

if img.Registry.Username != "" && img.Registry.Password != "" {
pushAuthConfig.Username = img.Registry.Username
pushAuthConfig.Password = img.Registry.Password
pushAuthConfig.ServerAddress, err = getRegistryAddr(img.Registry.Server, img.Name)
pushAuthConfig.ServerAddress, err = getRegistryAddrForAuth(img.Registry.Server, img.Name)
if err != nil {
return "", nil, err
}
Expand All @@ -204,19 +205,20 @@ func (p *dockerNativeProvider) dockerBuild(ctx context.Context,
return "", nil, err
}
}
// we push to the server declared in the program, using our auth configs from image build.
// if there is no servername in the registry, we attempt to build it from the fully qualified image name.
registryServer, err := getRegistryAddr(img.Registry.Server, img.Name)

registryServer, err := getRegistryAddrForAuth(img.Registry.Server, img.Name)
if err != nil {
return "", nil, err
}

// we use the credentials for the server declared in the program, looking them up from the host authConfigs.
pushAuthConfig = authConfigs[registryServer]
AaronFriel marked this conversation as resolved.
Show resolved Hide resolved
}

authConfigBytes, err := json.Marshal(pushAuthConfig)

if err != nil {
return "", nil, errors.Wrap(err, "Error parsing authConfig")
return "", nil, fmt.Errorf("error parsing authConfig: %v", err)
}
authConfigEncoded := base64.URLEncoding.EncodeToString(authConfigBytes)

Expand Down Expand Up @@ -411,7 +413,7 @@ func marshalBuilder(builder resource.PropertyValue) (types.BuilderVersion, error
default:
// because the Docker client will default to `BuilderV1`
// when version isn't set, we return an error
return version, errors.Errorf("Invalid Docker Builder version")
return version, fmt.Errorf("invalid Docker Builder version")
}
}

Expand All @@ -436,19 +438,24 @@ func getCredentials() (map[string]clitypes.AuthConfig, error) {
return auths, nil
}

func getRegistryAddr(serverName, imgName string) (string, error) {
// Because the authConfigs provided by the host include the `https://` prefix in the map keys, `getRegistryAddrForAuth`
// ensures we return a registry address that includes the `https://` scheme.
// While this prefix is not needed for program-provided auth, it is valid regardless, so adding it by default
// keeps the special case handling to a minimum.
func getRegistryAddrForAuth(serverName, imgName string) (string, error) {
var serverAddr string
if serverName == "docker.io" {
// if it's dockerhub, we special case it so host config can find the correct registry
return "https://index.docker.io/v1/", nil
}

if serverName == "" {
// construct server address from image name
addr, _, found := strings.Cut(imgName, "/")
if !found {
return "", errors.Errorf("image name must be fully qualified: %s", imgName)
// if there is no servername in the registry input, we attempt to build it from the fully qualified image name.
addr, err := getRegistryAddrFromImage(imgName)
if err != nil {
return "", err
}

if addr == "docker.io" {
return "https://index.docker.io/v1/", nil
}
Expand All @@ -457,29 +464,43 @@ func getRegistryAddr(serverName, imgName string) (string, error) {

} else {
// check if the provider registry server starts with https://
if strings.Contains(serverName, "https://") {
if strings.HasPrefix(serverName, "https://") {
serverAddr = serverName
} else {
// TODO: this is where we would default to Docker if we wanted to do so.
return "", errors.Errorf("invalid registry server name: %s", serverName)
// courtesy add the prefix so user does not have to explicitly do so
serverAddr = "https://" + serverName
}
}
return serverAddr, nil
}

func getRegistryAddrFromImage(imgName string) (string, error) {
named, err := reference.ParseNamed(imgName)
if err != nil {
msg := fmt.Errorf("error: %s. This provider requires all image names to be fully qualified.\n"+
"For example, if you are attempting to push to Dockerhub, prefix your image name with `docker.io`:\n\n"+
"`docker.io/repository/image:tag`", err)
fmt.Println(msg)
return "", err
}
addr := reference.Domain(named)
return addr, nil

}
AaronFriel marked this conversation as resolved.
Show resolved Hide resolved

func processLogLine(msg string) (string, error) {
var info string
var jm jsonmessage.JSONMessage
err := json.Unmarshal([]byte(msg), &jm)
if err != nil {
return info, errors.Wrapf(err, "encountered error unmarshalling:")
return info, fmt.Errorf("encountered error unmarshalling: %v", err)
}
// 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)
return info, fmt.Errorf(jm.Error.Message)
}
if jm.From != "" {
info += jm.From
Expand Down