build/mage/docker/tools.go (162 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package docker
import (
"fmt"
"os"
"strings"
"time"
"github.com/magefile/mage/mg"
"github.com/magefile/mage/sh"
"github.com/elastic/harp/build/mage/git"
exec "golang.org/x/sys/execabs"
)
var dockerToolTemplate = strings.TrimSpace(`
# syntax=docker/dockerfile:experimental
# Arguments
ARG BUILD_DATE={{.BuildDate}}
ARG VERSION={{.Version}}
ARG VCS_REF={{.VcsRef}}
# Builder arguments
ARG GOLANG_BASE_IMAGE={{.GolangImage}}
ARG GOLANG_VERSION={{.GolangVersion}}
## -------------------------------------------------------------------------------------------------
FROM ${GOLANG_BASE_IMAGE}
# Arguments
ARG BUILD_DATE={{.BuildDate}}
ARG VERSION={{.Version}}
ARG VCS_REF={{.VcsRef}}
# Builder arguments
ARG GOLANG_IMAGE={{.GolangImage}}
ARG GOLANG_VERSION={{.GolangVersion}}
ARG FIPS_MODE={{.FIPSMode}}
ENV HARP_BUILD_FIPS_MODE=$FIPS_MODE
LABEL \
org.opencontainers.image.created=$BUILD_DATE \
org.opencontainers.image.title="Harp SDK Environment (Go {{.GolangVersion}})" \
org.opencontainers.image.description="Harp SDK Tools used to build harp and all related tools" \
org.opencontainers.image.url="https://github.com/elastic/harp" \
org.opencontainers.image.source="https://github.com/elastic/harp.git" \
org.opencontainers.image.revision=$VCS_REF \
org.opencontainers.image.vendor="Elastic" \
org.opencontainers.image.version=$VERSION \
org.opencontainers.image.licences="ASL2"
{{ if .OverrideGoBoringVersion }}
# Override goboring version
RUN wget https://storage.googleapis.com/go-boringcrypto/go{{ .GoBoringVersion }}.linux-{{.GoArchitecture}}.tar.gz \
&& rm -rf /usr/local/go && tar -C /usr/local -xzf go{{ .GoBoringVersion }}.linux-{{.GoArchitecture}}.tar.gz \
&& rm go{{ .GoBoringVersion }}.linux-{{.GoArchitecture}}.tar.gz
{{ end }}
# hadolint ignore=DL3008
RUN set -eux; \
apt-get update -y && \
apt-get install -y --no-install-recommends apt-utils bzr zip unzip xz-utils;
RUN wget https://github.com/upx/upx/releases/download/v{{.UpxVersion}}/upx-{{.UpxVersion}}-{{.GoArchitecture}}_linux.tar.xz -o - \
&& tar -xJf upx-{{.UpxVersion}}-{{.GoArchitecture}}_linux.tar.xz \
&& mv upx-{{.UpxVersion}}-{{.GoArchitecture}}_linux/upx /usr/local/bin/upx
RUN go version
# Create a non-root privilege account to build
RUN adduser --disabled-password --gecos "" -u 1000 golang && \
mkdir -p "$GOPATH/src/workspace" && \
chown -R golang:golang "$GOPATH/src/workspace" && \
mkdir /home/golang/.ssh && \
mkdir /var/ssh && \
chown -R golang:golang /home/golang && \
chown -R golang:golang /var/ssh && \
chmod 700 /home/golang
# Force go modules
ENV GO111MODULE=on
# Disable go proxy
ENV GOPROXY=direct
ENV GOSUMDB=off
WORKDIR $GOPATH/src/workspace
# Prepare an unprivilegied user for run
RUN set -eux; \
echo 'nobody:x:65534:65534:nobody:/:' > /tmp/passwd && \
echo 'nobody:x:65534:' > /tmp/group && \
mkdir /tmp/.config && \
chown 65534:65534 /tmp/.config
# Drop privileges to build
USER golang
ENV USER golang
# Clean go mod cache
RUN set -eux; \
go clean -modcache
# Checkout mage
RUN set -eux; \
git clone https://github.com/magefile/mage .mage
# Go to tools
WORKDIR $GOPATH/src/workspace/.mage
# Install mage
RUN go run bootstrap.go
# Back to project root
WORKDIR $GOPATH/src/workspace
# Copy build tools
COPY --chown=golang:golang tools tools/
# Go to tools
WORKDIR $GOPATH/src/workspace/tools
# Install tools
RUN set -eux; \
mage
# Set path for tools usages
ENV PATH=$GOPATH/src/workspace/tools/bin:$PATH
`)
// Tools build a docker container used for compilation.
func Tools() error {
mg.Deps(git.CollectInfo)
// Retrieve golang attributes
golangBaseImage := golangImage
if os.Getenv("GOLANG_BASE_IMAGE") != "" {
golangBaseImage = os.Getenv("GOLANG_BASE_IMAGE")
}
golangVersion := golangVersion
if os.Getenv("GOLANG_VERSION") != "" {
golangVersion = os.Getenv("GOLANG_VERSION")
}
goBoringVersion := goBoringVersion
overrideGoBoringVersion := false
if os.Getenv("GOBORING_VERSION") != "" {
goBoringVersion = os.Getenv("GOBORING_VERSION")
overrideGoBoringVersion = true
}
fipsMode := "0"
if os.Getenv("FIPS_MODE") == "1" {
fipsMode = os.Getenv("FIPS_MODE")
}
buf, err := merge(dockerToolTemplate, map[string]interface{}{
"BuildDate": time.Now().Format(time.RFC3339),
"Version": git.Tag,
"VcsRef": git.Revision,
"GolangImage": golangBaseImage,
"GolangVersion": golangVersion,
"GoArchitecture": goArchitecture,
"OverrideGoBoringVersion": overrideGoBoringVersion,
"GoBoringVersion": goBoringVersion,
"FIPSMode": fipsMode,
"UpxVersion": upxVersion,
})
if err != nil {
return err
}
// Check if we want to generate dockerfile output
if os.Getenv("DOCKERFILE_ONLY") != "" {
return os.WriteFile("Dockerfile.tools", buf.Bytes(), 0o600)
}
// Docker image name
dockerImageName := toolImage
if os.Getenv("DOCKER_IMAGE_NAME") != "" {
dockerImageName = os.Getenv("DOCKER_IMAGE_NAME")
}
// Prepare command
c := exec.Command("docker", "build",
"-t", dockerImageName,
"-f", "-",
".",
)
// Prepare environment
c.Env = os.Environ()
c.Env = append(c.Env, "DOCKER_BUILDKIT=1")
c.Stderr = os.Stderr
c.Stdout = os.Stdout
c.Stdin = buf // Pass Dockerfile as stdin
// Execute
err = c.Run()
if err != nil {
return fmt.Errorf("unable to run command: %w", err)
}
// Check execution error
if !sh.CmdRan(err) {
return fmt.Errorf("running '%s' failed with exit code %d", c.String(), sh.ExitStatus(err))
}
return err
}