2021-08-18 12:10:55 +02:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
2021-09-27 10:32:05 +02:00
|
|
|
"archive/zip"
|
2021-08-18 12:10:55 +02:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path"
|
2021-10-01 10:05:15 +02:00
|
|
|
"path/filepath"
|
2021-08-26 14:26:54 +02:00
|
|
|
"regexp"
|
2021-08-18 12:10:55 +02:00
|
|
|
"strings"
|
|
|
|
|
2021-10-01 13:48:24 +02:00
|
|
|
"github.com/SAP/jenkins-library/pkg/certutils"
|
2021-09-14 16:14:50 +02:00
|
|
|
"github.com/SAP/jenkins-library/pkg/cnbutils"
|
2021-08-18 12:10:55 +02:00
|
|
|
"github.com/SAP/jenkins-library/pkg/command"
|
|
|
|
"github.com/SAP/jenkins-library/pkg/docker"
|
2021-10-01 13:48:24 +02:00
|
|
|
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
2021-08-18 12:10:55 +02:00
|
|
|
"github.com/SAP/jenkins-library/pkg/log"
|
|
|
|
"github.com/SAP/jenkins-library/pkg/piperutils"
|
|
|
|
"github.com/SAP/jenkins-library/pkg/telemetry"
|
|
|
|
"github.com/docker/cli/cli/config/configfile"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
2021-09-14 15:38:58 +02:00
|
|
|
const (
|
|
|
|
detectorPath = "/cnb/lifecycle/detector"
|
|
|
|
builderPath = "/cnb/lifecycle/builder"
|
|
|
|
exporterPath = "/cnb/lifecycle/exporter"
|
|
|
|
)
|
|
|
|
|
2021-08-18 12:10:55 +02:00
|
|
|
type cnbBuildUtilsBundle struct {
|
|
|
|
*command.Command
|
|
|
|
*piperutils.Files
|
2021-09-14 16:14:50 +02:00
|
|
|
*docker.Client
|
|
|
|
}
|
|
|
|
|
2021-10-01 10:05:15 +02:00
|
|
|
func setCustomBuildpacks(bpacks []string, dockerCreds string, utils cnbutils.BuildUtils) (string, string, error) {
|
2021-09-14 16:14:50 +02:00
|
|
|
buildpacksPath := "/tmp/buildpacks"
|
|
|
|
orderPath := "/tmp/buildpacks/order.toml"
|
2021-10-01 10:05:15 +02:00
|
|
|
newOrder, err := cnbutils.DownloadBuildpacks(buildpacksPath, bpacks, dockerCreds, utils)
|
2021-09-14 16:14:50 +02:00
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = newOrder.Save(orderPath)
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return buildpacksPath, orderPath, nil
|
2021-08-18 12:10:55 +02:00
|
|
|
}
|
|
|
|
|
2021-09-14 16:14:50 +02:00
|
|
|
func newCnbBuildUtils() cnbutils.BuildUtils {
|
2021-08-18 12:10:55 +02:00
|
|
|
utils := cnbBuildUtilsBundle{
|
|
|
|
Command: &command.Command{},
|
|
|
|
Files: &piperutils.Files{},
|
2021-09-14 16:14:50 +02:00
|
|
|
Client: &docker.Client{},
|
2021-08-18 12:10:55 +02:00
|
|
|
}
|
|
|
|
utils.Stdout(log.Writer())
|
|
|
|
utils.Stderr(log.Writer())
|
|
|
|
return &utils
|
|
|
|
}
|
|
|
|
|
|
|
|
func cnbBuild(config cnbBuildOptions, telemetryData *telemetry.CustomData, commonPipelineEnvironment *cnbBuildCommonPipelineEnvironment) {
|
|
|
|
utils := newCnbBuildUtils()
|
|
|
|
|
2021-10-01 13:48:24 +02:00
|
|
|
client := &piperhttp.Client{}
|
|
|
|
|
|
|
|
err := runCnbBuild(&config, telemetryData, utils, commonPipelineEnvironment, client)
|
2021-08-18 12:10:55 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Entry().WithError(err).Fatal("step execution failed")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func isIgnored(find string) bool {
|
|
|
|
return strings.HasSuffix(find, "piper") || strings.Contains(find, ".pipeline")
|
|
|
|
}
|
|
|
|
|
|
|
|
func isDir(path string) (bool, error) {
|
|
|
|
info, err := os.Stat(path)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return info.IsDir(), nil
|
|
|
|
}
|
|
|
|
|
2021-09-14 16:14:50 +02:00
|
|
|
func isBuilder(utils cnbutils.BuildUtils) (bool, error) {
|
2021-09-14 15:38:58 +02:00
|
|
|
for _, path := range []string{detectorPath, builderPath, exporterPath} {
|
|
|
|
exists, err := utils.FileExists(path)
|
|
|
|
if err != nil || !exists {
|
|
|
|
return exists, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2021-09-27 10:32:05 +02:00
|
|
|
func isZip(path string) bool {
|
|
|
|
r, err := zip.OpenReader(path)
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case err == nil:
|
|
|
|
r.Close()
|
|
|
|
return true
|
|
|
|
case err == zip.ErrFormat:
|
|
|
|
return false
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func copyProject(source, target string, utils cnbutils.BuildUtils) error {
|
|
|
|
sourceFiles, _ := utils.Glob(path.Join(source, "**"))
|
|
|
|
for _, sourceFile := range sourceFiles {
|
|
|
|
if !isIgnored(sourceFile) {
|
|
|
|
target := path.Join(target, strings.ReplaceAll(sourceFile, source, ""))
|
|
|
|
dir, err := isDir(sourceFile)
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorBuild)
|
|
|
|
return errors.Wrapf(err, "Checking file info '%s' failed", target)
|
|
|
|
}
|
|
|
|
|
|
|
|
if dir {
|
|
|
|
err = utils.MkdirAll(target, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorBuild)
|
|
|
|
return errors.Wrapf(err, "Creating directory '%s' failed", target)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log.Entry().Debugf("Copying '%s' to '%s'", sourceFile, target)
|
|
|
|
_, err = utils.Copy(sourceFile, target)
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorBuild)
|
|
|
|
return errors.Wrapf(err, "Copying '%s' to '%s' failed", sourceFile, target)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
log.Entry().Debugf("Filtered out '%s'", sourceFile)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func copyFile(source, target string, utils cnbutils.BuildUtils) error {
|
|
|
|
|
|
|
|
if isZip(source) {
|
|
|
|
log.Entry().Infof("Extracting archive '%s' to '%s'", source, target)
|
|
|
|
_, err := piperutils.Unzip(source, target)
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorBuild)
|
|
|
|
return errors.Wrapf(err, "Extracting archive '%s' to '%s' failed", source, target)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log.SetErrorCategory(log.ErrorBuild)
|
|
|
|
return errors.New("application path must be a directory or zip")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-10-01 10:05:15 +02:00
|
|
|
func prepareDockerConfig(source string, utils cnbutils.BuildUtils) (string, error) {
|
|
|
|
if filepath.Base(source) != "config.json" {
|
|
|
|
log.Entry().Debugf("Renaming docker config file from '%s' to 'config.json'", filepath.Base(source))
|
|
|
|
|
|
|
|
newPath := filepath.Join(filepath.Dir(source), "config.json")
|
|
|
|
err := utils.FileRename(source, newPath)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return newPath, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return source, nil
|
|
|
|
}
|
|
|
|
|
2021-10-01 13:48:24 +02:00
|
|
|
func runCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, utils cnbutils.BuildUtils, commonPipelineEnvironment *cnbBuildCommonPipelineEnvironment, httpClient piperhttp.Sender) error {
|
2021-08-18 12:10:55 +02:00
|
|
|
var err error
|
|
|
|
|
2021-09-14 15:38:58 +02:00
|
|
|
exists, err := isBuilder(utils)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorConfiguration)
|
|
|
|
return errors.Wrapf(err, "failed to check if dockerImage is a valid builder")
|
|
|
|
}
|
|
|
|
if !exists {
|
|
|
|
log.SetErrorCategory(log.ErrorConfiguration)
|
|
|
|
return errors.New("the provided dockerImage is not a valid builder")
|
|
|
|
}
|
|
|
|
|
2021-09-29 18:21:13 +02:00
|
|
|
platformPath := "/platform"
|
|
|
|
if config.BuildEnvVars != nil && len(config.BuildEnvVars) > 0 {
|
|
|
|
log.Entry().Infof("Setting custom environment variables: '%v'", config.BuildEnvVars)
|
|
|
|
platformPath = "/tmp/platform"
|
|
|
|
err = cnbutils.CreateEnvFiles(utils, platformPath, config.BuildEnvVars)
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorConfiguration)
|
|
|
|
return errors.Wrap(err, "failed to write environment variables to files")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-01 10:05:15 +02:00
|
|
|
dockerConfigFile := ""
|
2021-08-18 12:10:55 +02:00
|
|
|
dockerConfig := &configfile.ConfigFile{}
|
|
|
|
dockerConfigJSON := []byte(`{"auths":{}}`)
|
|
|
|
if len(config.DockerConfigJSON) > 0 {
|
2021-10-01 10:05:15 +02:00
|
|
|
dockerConfigFile, err = prepareDockerConfig(config.DockerConfigJSON, utils)
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorConfiguration)
|
|
|
|
return errors.Wrapf(err, "failed to rename DockerConfigJSON file '%v'", config.DockerConfigJSON)
|
|
|
|
}
|
|
|
|
dockerConfigJSON, err = utils.FileRead(dockerConfigFile)
|
2021-08-18 12:10:55 +02:00
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorConfiguration)
|
|
|
|
return errors.Wrapf(err, "failed to read DockerConfigJSON file '%v'", config.DockerConfigJSON)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(dockerConfigJSON, dockerConfig)
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorConfiguration)
|
|
|
|
return errors.Wrapf(err, "failed to parse DockerConfigJSON file '%v'", config.DockerConfigJSON)
|
|
|
|
}
|
|
|
|
|
|
|
|
auth := map[string]string{}
|
|
|
|
for registry, value := range dockerConfig.AuthConfigs {
|
|
|
|
auth[registry] = fmt.Sprintf("Basic %s", value.Auth)
|
|
|
|
}
|
|
|
|
|
|
|
|
cnbRegistryAuth, err := json.Marshal(auth)
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorConfiguration)
|
|
|
|
return errors.Wrap(err, "failed to marshal DockerConfigJSON")
|
|
|
|
}
|
|
|
|
|
|
|
|
target := "/workspace"
|
|
|
|
source, err := utils.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorBuild)
|
|
|
|
return errors.Wrap(err, "failed to get current working directory")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(config.Path) > 0 {
|
|
|
|
source = config.Path
|
|
|
|
}
|
|
|
|
|
2021-09-27 10:32:05 +02:00
|
|
|
dir, err := isDir(source)
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorBuild)
|
|
|
|
return errors.Wrapf(err, "Checking file info '%s' failed", target)
|
|
|
|
}
|
2021-08-18 12:10:55 +02:00
|
|
|
|
2021-09-27 10:32:05 +02:00
|
|
|
if dir {
|
|
|
|
err = copyProject(source, target, utils)
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorBuild)
|
|
|
|
return errors.Wrapf(err, "Copying '%s' into '%s' failed", source, target)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err = copyFile(source, target, utils)
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorBuild)
|
|
|
|
return errors.Wrapf(err, "Copying '%s' into '%s' failed", source, target)
|
2021-08-18 12:10:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-14 16:14:50 +02:00
|
|
|
var buildpacksPath = "/cnb/buildpacks"
|
|
|
|
var orderPath = "/cnb/order.toml"
|
|
|
|
|
2021-09-29 18:21:13 +02:00
|
|
|
if config.Buildpacks != nil && len(config.Buildpacks) > 0 {
|
2021-09-14 16:14:50 +02:00
|
|
|
log.Entry().Infof("Setting custom buildpacks: '%v'", config.Buildpacks)
|
2021-10-01 10:05:15 +02:00
|
|
|
buildpacksPath, orderPath, err = setCustomBuildpacks(config.Buildpacks, dockerConfigFile, utils)
|
2021-09-14 16:14:50 +02:00
|
|
|
defer utils.RemoveAll(buildpacksPath)
|
|
|
|
defer utils.RemoveAll(orderPath)
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorBuild)
|
|
|
|
return errors.Wrapf(err, "Setting custom buildpacks: %v", config.Buildpacks)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-18 12:10:55 +02:00
|
|
|
var containerImage string
|
|
|
|
var containerImageTag string
|
|
|
|
|
|
|
|
if len(config.ContainerRegistryURL) > 0 && len(config.ContainerImageName) > 0 && len(config.ContainerImageTag) > 0 {
|
2021-08-26 14:26:54 +02:00
|
|
|
var containerRegistry string
|
|
|
|
if matched, _ := regexp.MatchString("^(http|https)://.*", config.ContainerRegistryURL); matched {
|
|
|
|
containerRegistry, err = docker.ContainerRegistryFromURL(config.ContainerRegistryURL)
|
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorConfiguration)
|
|
|
|
return errors.Wrapf(err, "failed to read containerRegistryUrl %s", config.ContainerRegistryURL)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
containerRegistry = config.ContainerRegistryURL
|
2021-08-18 12:10:55 +02:00
|
|
|
}
|
2021-08-26 14:26:54 +02:00
|
|
|
|
2021-08-18 12:10:55 +02:00
|
|
|
containerImage = fmt.Sprintf("%s/%s", containerRegistry, config.ContainerImageName)
|
|
|
|
containerImageTag = strings.ReplaceAll(config.ContainerImageTag, "+", "-")
|
|
|
|
commonPipelineEnvironment.container.registryURL = config.ContainerRegistryURL
|
|
|
|
commonPipelineEnvironment.container.imageNameTag = containerImage
|
|
|
|
} else {
|
|
|
|
log.SetErrorCategory(log.ErrorConfiguration)
|
2021-08-26 14:26:54 +02:00
|
|
|
return errors.New("containerRegistryUrl, containerImageName and containerImageTag must be present")
|
2021-08-18 12:10:55 +02:00
|
|
|
}
|
|
|
|
|
2021-10-01 13:48:24 +02:00
|
|
|
if len(config.CustomTLSCertificateLinks) > 0 {
|
2021-10-07 16:04:20 +02:00
|
|
|
caCertificates := "/tmp/ca-certificates.crt"
|
|
|
|
_, err := utils.Copy("/etc/ssl/certs/ca-certificates.crt", caCertificates)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "failed to copy certificates")
|
|
|
|
}
|
|
|
|
err = certutils.CertificateUpdate(config.CustomTLSCertificateLinks, httpClient, utils, caCertificates)
|
2021-10-01 13:48:24 +02:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "failed to update certificates")
|
|
|
|
}
|
2021-10-07 16:04:20 +02:00
|
|
|
utils.AppendEnv([]string{fmt.Sprintf("SSL_CERT_FILE=%s", caCertificates)})
|
2021-10-01 13:48:24 +02:00
|
|
|
} else {
|
|
|
|
log.Entry().Info("skipping updation of certificates")
|
|
|
|
}
|
|
|
|
|
2021-09-29 18:21:13 +02:00
|
|
|
err = utils.RunExecutable(detectorPath, "-buildpacks", buildpacksPath, "-order", orderPath, "-platform", platformPath)
|
2021-08-18 12:10:55 +02:00
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorBuild)
|
2021-09-27 10:32:05 +02:00
|
|
|
return errors.Wrapf(err, "execution of '%s' failed", detectorPath)
|
2021-08-18 12:10:55 +02:00
|
|
|
}
|
|
|
|
|
2021-09-29 18:21:13 +02:00
|
|
|
err = utils.RunExecutable(builderPath, "-buildpacks", buildpacksPath, "-platform", platformPath)
|
2021-08-18 12:10:55 +02:00
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorBuild)
|
2021-09-27 10:32:05 +02:00
|
|
|
return errors.Wrapf(err, "execution of '%s' failed", builderPath)
|
2021-08-18 12:10:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
utils.AppendEnv([]string{fmt.Sprintf("CNB_REGISTRY_AUTH=%s", string(cnbRegistryAuth))})
|
2021-09-14 15:38:58 +02:00
|
|
|
err = utils.RunExecutable(exporterPath, fmt.Sprintf("%s:%s", containerImage, containerImageTag), fmt.Sprintf("%s:latest", containerImage))
|
2021-08-18 12:10:55 +02:00
|
|
|
if err != nil {
|
|
|
|
log.SetErrorCategory(log.ErrorBuild)
|
2021-09-27 10:32:05 +02:00
|
|
|
return errors.Wrapf(err, "execution of '%s' failed", exporterPath)
|
2021-08-18 12:10:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|