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

automate the ironbank generation #8537

Merged
merged 18 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from 8 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: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ APM_AGENT_JAVA_PUB_KEY:=apm-agent-java-public-key.asc
release: export PATH:=$(dir $(BIN_MAGE)):$(PATH)
release: $(MAGE) $(PYTHON) build/$(JAVA_ATTACHER_JAR) build/dependencies.csv $(APM_SERVER_BINARIES)
@$(MAGE) package
@$(MAGE) ironbank

build/dependencies.csv: $(PYTHON) go.mod
$(PYTHON) script/generate_notice.py ./x-pack/apm-server --csv $@
Expand Down
173 changes: 173 additions & 0 deletions magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@
package main

import (
"archive/tar"
"compress/gzip"
"context"
"fmt"
"io"
"log"
"os"
"path/filepath"
Expand Down Expand Up @@ -140,6 +143,20 @@ func filterPackages(types string) {
mage.Packages = packages
}

// Package packages apm-server for the IronBank distribution, relying on the
// binaries having already been built.
//
// Use SNAPSHOT=true to build snapshots.
func Ironbank() error {
if err := prepareIronbankBuild(); err != nil {
return errors.Wrap(err, "failed to prepare build")
}
if err := saveIronbank(); err != nil {
return errors.Wrap(err, "failed to save artifacts for ironbank")
}
return nil
}

// Package packages apm-server for distribution, relying on the
// binaries having already been built.
//
Expand Down Expand Up @@ -237,6 +254,162 @@ func customizePackaging() {
}
}

func Tar(source, target string) error {
filename := filepath.Base(source)
target = filepath.Join(target, fmt.Sprintf("%s.tar", filename))
tarfile, err := os.Create(target)
if err != nil {
return err
}
defer tarfile.Close()

tarball := tar.NewWriter(tarfile)
defer tarball.Close()

info, err := os.Stat(source)
if err != nil {
return nil
}

var baseDir string
if info.IsDir() {
baseDir = filepath.Base(source)
}

return filepath.Walk(source,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := tar.FileInfoHeader(info, info.Name())
if err != nil {
return err
}

if baseDir != "" {
header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
}

if err := tarball.WriteHeader(header); err != nil {
return err
}

if info.IsDir() {
return nil
}

file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(tarball, file)
return err
})
}

func Gzip(source, target string) error {
reader, err := os.Open(source)
if err != nil {
return err
}

filename := filepath.Base(source)
target = filepath.Join(target, fmt.Sprintf("%s.gz", filename))
writer, err := os.Create(target)
if err != nil {
return err
}
defer writer.Close()

archiver := gzip.NewWriter(writer)
archiver.Name = filename
defer archiver.Close()

_, err = io.Copy(archiver, reader)
return err
}

func saveIronbank() error {
ironbank := getIronbankContextName()
buildDir := filepath.Join("build", ironbank)
if _, err := os.Stat(buildDir); os.IsNotExist(err) {
return fmt.Errorf("cannot find the folder with the ironbank context")
}

distributionsDir := "build/distributions"
if _, err := os.Stat(distributionsDir); os.IsNotExist(err) {
err := os.MkdirAll(distributionsDir, 0750)
if err != nil {
return fmt.Errorf("cannot create folder for docker artifacts: %+v", err)
}
}

// Save the build context as artifact
Tar(buildDir, distributionsDir)
source := filepath.Join(distributionsDir, ironbank)
tarSource := source + ".tar"
Gzip(tarSource, distributionsDir)
tarGzSource := tarSource + ".gz"

// Remove leftovers
if err := os.RemoveAll(source); err != nil {
return errors.Wrapf(err, "failed to clean existing build directory %s", source)
}
if err := os.RemoveAll(tarSource); err != nil {
return errors.Wrapf(err, "failed to clean existing build directory %s", tarSource)
}

return errors.Wrap(mage.CreateSHA512File(tarGzSource), "failed to create .sha512 file")
}

func getIronbankContextName() string {
version, _ := mage.BeatQualifiedVersion()
defaultBinaryName := "{{.Name}}-ironbank-{{.Version}}{{if .Snapshot}}-SNAPSHOT{{end}}"
outputDir, _ := mage.Expand(defaultBinaryName+"-docker-build-context", map[string]interface{}{
"Name": "apm-server",
"Version": version,
})
return outputDir
}

func prepareIronbankBuild() error {
ironbank := getIronbankContextName()
templatesDir := filepath.Join("packaging", "ironbank")

data := map[string]interface{}{
"MajorMinor": majorMinor(),
}

err := filepath.Walk(templatesDir, func(path string, info os.FileInfo, _ error) error {
if !info.IsDir() {
target := strings.TrimSuffix(
filepath.Join("build", ironbank, filepath.Base(path)),
".tmpl",
)

err := mage.ExpandFile(path, target, data)
if err != nil {
return errors.Wrapf(err, "expanding template '%s' to '%s'", path, target)
}
}
return nil
})

if err != nil {
return err
}
return nil
}

func majorMinor() string {
if v, _ := mage.BeatQualifiedVersion(); v != "" {
parts := strings.SplitN(v, ".", 3)
return parts[0] + "." + parts[1]
}
return ""
}

func Check() error {
fmt.Println(">> check: Checking source code for common problems")

Expand Down
74 changes: 74 additions & 0 deletions packaging/ironbank/Dockerfile.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
################################################################################
# Build stage 0
# Extract APM Server and make various file manipulations.
################################################################################
ARG BASE_REGISTRY=registry1.dsop.io
ARG BASE_IMAGE=ironbank/redhat/ubi/ubi8
ARG BASE_TAG=8.6

FROM ${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG} AS builder

ARG ELASTIC_STACK={{ beat_version }}
ARG ELASTIC_PRODUCT=apm-server
ARG OS_AND_ARCH=linux-x86_64

RUN mkdir /usr/share/${ELASTIC_PRODUCT}
WORKDIR /usr/share/${ELASTIC_PRODUCT}
COPY --chown=1000:0 ${ELASTIC_PRODUCT}-${ELASTIC_STACK}-${OS_AND_ARCH}.tar.gz .
RUN tar --strip-components=1 -zxf ${ELASTIC_PRODUCT}-${ELASTIC_STACK}-${OS_AND_ARCH}.tar.gz
# Support arbitrary user ids
# Ensure that group permissions are the same as user permissions.
# This will help when relying on GID-0 to run Kibana, rather than UID-1000.
# OpenShift does this, for example.
# REF: https://docs.okd.io/latest/openshift_images/create-images.html
RUN chmod -R g=u /usr/share/${ELASTIC_PRODUCT}

# Create auxiliar folders and assing default permissions.
RUN mkdir /usr/share/${ELASTIC_PRODUCT}/data /usr/share/${ELASTIC_PRODUCT}/logs && \
chown -R root:root /usr/share/${ELASTIC_PRODUCT} && \
find /usr/share/${ELASTIC_PRODUCT} -type d -exec chmod 0750 {} \; && \
find /usr/share/${ELASTIC_PRODUCT} -type f -exec chmod 0640 {} \; && \
chmod 0750 /usr/share/${ELASTIC_PRODUCT}/${ELASTIC_PRODUCT} && \
chmod 0770 /usr/share/${ELASTIC_PRODUCT}/data /usr/share/${ELASTIC_PRODUCT}/logs

################################################################################
# Build stage 1
# Copy prepared files from the previous stage and complete the image.
################################################################################
FROM ${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG}

ARG ELASTIC_PRODUCT=apm-server

COPY LICENSE /licenses/elastic-${ELASTIC_PRODUCT}

# Add a dumb init process
COPY tinit /tinit
RUN chmod +x /tinit

# Bring in product from the initial stage.
COPY --from=prep_files --chown=1000:0 /usr/share/${ELASTIC_PRODUCT} /usr/share/${ELASTIC_PRODUCT}
WORKDIR /usr/share/${ELASTIC_PRODUCT}
RUN ln -s /usr/share/${ELASTIC_PRODUCT} /opt/${ELASTIC_PRODUCT}

ENV ELASTIC_CONTAINER true
RUN ln -s /usr/share/${ELASTIC_PRODUCT}/${ELASTIC_PRODUCT} /usr/bin/${ELASTIC_PRODUCT}

# Support arbitrary user ids
# Ensure gid 0 write permissions for OpenShift.
RUN chmod -R g+w /usr/share/${ELASTIC_PRODUCT}

# config file ("${ELASTIC_PRODUCT}.yml") can only be writable by the owner
RUN chmod go-w /usr/share/${ELASTIC_PRODUCT}/${ELASTIC_PRODUCT}.yml

# Remove the suid bit everywhere to mitigate "Stack Clash"
RUN find / -xdev -perm -4000 -exec chmod u-s {} +

# Provide a non-root user to run the process.
RUN groupadd --gid 1000 ${ELASTIC_PRODUCT} && useradd --uid 1000 --gid 1000 --home-dir /usr/share/${ELASTIC_PRODUCT} --no-create-home ${ELASTIC_PRODUCT}
USER ${ELASTIC_PRODUCT}

EXPOSE 8200
ENTRYPOINT ["/tinit", "--", "/usr/share/apm-server/apm-server"]
CMD ["-environment", "container"]

HEALTHCHECK --interval=10s --timeout=5s --start-period=1m --retries=5 CMD (curl -I -f --max-time 5 https://localhost:8200 || curl -I -f --max-time 5 http://localhost:8200 || exit 1)
Loading