220 lines
4.6 KiB
Go
220 lines
4.6 KiB
Go
package docker
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
|
|
"github.com/docker/cli/cli/config/configfile"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/filters"
|
|
"github.com/docker/docker/client"
|
|
"github.com/docker/docker/pkg/archive"
|
|
"github.com/docker/docker/pkg/jsonmessage"
|
|
"github.com/moby/term"
|
|
|
|
log2 "git.indra-labs.org/dev/ind/pkg/proc/log"
|
|
)
|
|
|
|
var (
|
|
log = log2.GetLogger()
|
|
check = log.E.Chk
|
|
)
|
|
|
|
var (
|
|
isRelease = false
|
|
isPushable = false
|
|
)
|
|
|
|
// SetRelease enables releasing of docker images.
|
|
func SetRelease() {
|
|
isRelease = true
|
|
}
|
|
|
|
// SetPush enables pushing of dockre images.
|
|
func SetPush() {
|
|
isPushable = true
|
|
}
|
|
|
|
// Builder is a data structure defining a docker build.
|
|
type Builder struct {
|
|
*client.Client
|
|
ctx context.Context
|
|
configs []BuildConfiguration
|
|
source_configs []BuildConfiguration
|
|
pkg_configs []BuildConfiguration
|
|
}
|
|
|
|
func (b *Builder) build(buildConfig BuildConfiguration) (err error) {
|
|
|
|
// We need the absolute path for build tags to be valid
|
|
buildConfig.BuildOpts.Tags = buildConfig.FixTagPrefix()
|
|
|
|
log.I.Ln("building", buildConfig.BuildOpts.Tags[0], "from", buildConfig.BuildOpts.Dockerfile)
|
|
|
|
// If we're building a release, we should also tag stable.
|
|
|
|
if isRelease {
|
|
buildConfig.BuildOpts.Tags = append(buildConfig.BuildOpts.Tags, "stable")
|
|
}
|
|
|
|
// Generate a tar file for docker's release context. It will contain the root of the repository's path.
|
|
// A tar file is passed in to the docker daemon.
|
|
|
|
var tar io.ReadCloser
|
|
|
|
if tar, err = archive.TarWithOptions(".", &archive.TarOptions{}); check(err) {
|
|
return
|
|
}
|
|
|
|
defer tar.Close()
|
|
|
|
// Submit a release to docker; with the context tar, and default options defined above.
|
|
|
|
log.I.Ln("running build with docker...")
|
|
|
|
var response types.ImageBuildResponse
|
|
|
|
if response, err = b.ImageBuild(b.ctx, tar, buildConfig.BuildOpts); check(err) {
|
|
return
|
|
}
|
|
|
|
defer response.Body.Close()
|
|
|
|
// Generate a terminal for output
|
|
|
|
termFd, isTerm := term.GetFdInfo(os.Stderr)
|
|
|
|
if err = jsonmessage.DisplayJSONMessagesStream(response.Body, os.Stderr, termFd, isTerm, nil); check(err) {
|
|
return
|
|
}
|
|
|
|
// Prune the intermediate golang:x.xx builder container
|
|
|
|
log.I.Ln("pruning release container(s)...")
|
|
|
|
if _, err = b.ImagesPrune(b.ctx, filters.NewArgs()); check(err) {
|
|
return
|
|
}
|
|
|
|
log.I.Ln("pruning successful.")
|
|
log.I.Ln("release successful!")
|
|
|
|
return
|
|
}
|
|
|
|
// Build runs a Builder specification.
|
|
func (b *Builder) Build() (err error) {
|
|
|
|
for _, buildConfig := range b.source_configs {
|
|
|
|
if err = b.build(buildConfig); check(err) {
|
|
return
|
|
}
|
|
}
|
|
|
|
for _, buildConfig := range b.configs {
|
|
|
|
if err = b.build(buildConfig); check(err) {
|
|
return
|
|
}
|
|
}
|
|
|
|
for _, buildConfig := range b.pkg_configs {
|
|
|
|
if err = b.build(buildConfig); check(err) {
|
|
return
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (b *Builder) push(buildConfig BuildConfiguration) (err error) {
|
|
|
|
if !isPushable {
|
|
return nil
|
|
}
|
|
|
|
log.I.Ln("pushing tagged images to repository...")
|
|
|
|
// Load the docker config
|
|
|
|
var file []byte
|
|
var config *configfile.ConfigFile
|
|
|
|
if file, err = ioutil.ReadFile(os.Getenv("INDRA_DOCKER_CONFIG")); check(err) {
|
|
return
|
|
}
|
|
|
|
config = configfile.New("config.json")
|
|
|
|
config.LoadFromReader(bytes.NewReader(file))
|
|
|
|
// Generate a terminal for output
|
|
|
|
termFd, isTerm := term.GetFdInfo(os.Stderr)
|
|
|
|
// Push the specified tags to each docker repository
|
|
|
|
var pushResponse io.ReadCloser
|
|
|
|
for _, auth := range config.AuthConfigs {
|
|
|
|
log.I.Ln("found", auth.ServerAddress)
|
|
|
|
// Generate an authentication token
|
|
|
|
authConfigBytes, _ := json.Marshal(auth)
|
|
|
|
buildConfig.PushOpts.RegistryAuth = base64.URLEncoding.EncodeToString(authConfigBytes)
|
|
|
|
// Pushes each tag to the docker repository.
|
|
|
|
for _, tag := range buildConfig.FixTagPrefix() {
|
|
|
|
log.I.Ln("pushing", tag)
|
|
|
|
if pushResponse, err = b.ImagePush(b.ctx, tag, buildConfig.PushOpts); check(err) {
|
|
return
|
|
}
|
|
|
|
if err = jsonmessage.DisplayJSONMessagesStream(pushResponse, os.Stderr, termFd, isTerm, nil); check(err) {
|
|
return
|
|
}
|
|
|
|
if err = pushResponse.Close(); check(err) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
log.I.Ln("sucessfully pushed!")
|
|
|
|
return nil
|
|
}
|
|
|
|
// Push a build to the configured docker registry/repository.
|
|
func (b *Builder) Push() (err error) {
|
|
|
|
for _, buildConfig := range b.configs {
|
|
|
|
if err = b.push(buildConfig); check(err) {
|
|
return
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// NewBuilder bundles a builder together from its parts.
|
|
func NewBuilder(ctx context.Context, cli *client.Client, sourceConfigs []BuildConfiguration,
|
|
buildConfigs []BuildConfiguration, pkgConfigs []BuildConfiguration) (builder *Builder) {
|
|
|
|
return &Builder{cli, ctx, buildConfigs, sourceConfigs, pkgConfigs}
|
|
}
|