teleport/dronegen/common.go
Cam Hutchison edf2202d13
dronegen: Build Teleport Connect for amd64 push build (#29622)
* dronegen: Build Teleport Connect for amd64 push build

Add an input parameter when calling the release-linux workflow to build
Teleport Connect for the AMD64 build. This was previously done when
Drone was doing the build but got accidentally dropped when moving to
GitHub actions.

This will also be used for the tag builds when they migrate to GHA as we
do a release build of Teleport Connect for each architecture.

* ci: Update .drone.yml

Update .drone.yml with `make dronegen` to add the `build-connect`
parameter to the call of the `release-linux` workflow.

* Update e ref for release-linux teleport param

Update e ref for 98fc02c3f276054b72fe7c55544b45834d964b9b so we can call
the release-linux workflow with the `build-connect` parameter.
2023-08-01 04:32:48 +00:00

348 lines
9.3 KiB
Go

// Copyright 2021 Gravitational, Inc
//
// Licensed 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 main
import (
"bytes"
"fmt"
"log"
"os/exec"
"strings"
)
const (
// StagingRegistry is the staging registry images are pushed to before being promoted to the production registry.
StagingRegistry = "146628656107.dkr.ecr.us-west-2.amazonaws.com"
// ProductionRegistry is the production image registry that hosts are customer facing container images.
ProductionRegistry = "public.ecr.aws"
// Go version used by internal tools
GoVersion = "1.18"
// The name of this service must match k8s.io/apimachinery/pkg/util/validation `IsDNS1123Subdomain`
// so that it is resolvable
// See https://github.com/drone-runners/drone-runner-kube/blob/master/engine/compiler/compiler.go#L398
// for details
LocalRegistryHostname string = "drone-docker-registry"
LocalRegistrySocket string = LocalRegistryHostname + ":5000"
)
var (
triggerPush = trigger{
Event: triggerRef{Include: []string{"push"}, Exclude: []string{"pull_request"}},
Branch: triggerRef{Include: []string{"master", "branch/*"}},
Repo: triggerRef{Include: []string{"gravitational/*"}},
}
triggerTag = trigger{
Event: triggerRef{Include: []string{"tag"}},
Ref: triggerRef{Include: []string{"refs/tags/v*"}},
Repo: triggerRef{Include: []string{"gravitational/*"}},
}
triggerPromote = trigger{
Event: triggerRef{Include: []string{"promote"}},
Target: triggerRef{Include: []string{"production"}},
Repo: triggerRef{Include: []string{"gravitational/*"}},
}
volumeDocker = volume{
Name: "dockersock",
Temp: &volumeTemp{},
}
volumeRefDocker = volumeRef{
Name: "dockersock",
Path: "/var/run",
}
volumeTmpfs = volume{
Name: "tmpfs",
Temp: &volumeTemp{Medium: "memory"},
}
volumeRefTmpfs = volumeRef{
Name: "tmpfs",
Path: "/tmpfs",
}
volumeAwsConfig = volume{
Name: "awsconfig",
Temp: &volumeTemp{},
}
volumeRefAwsConfig = volumeRef{
Name: "awsconfig",
Path: "/root/.aws",
}
// volumeDockerConfig is a temporary volume for storing docker
// credentials for use with the Docker-in-Docker service we use
// to isolate the host machines docker daemon from the one used
// during the build. Mount this any time you use `volumeDocker`
//
// Drone claims to destroy the the temp volumes after a workflow
// has run, so it should be safe to write credentials etc.
volumeDockerConfig = volume{
Name: "dockerconfig",
Temp: &volumeTemp{},
}
// volumeRefDockerConfig is how you reference the docker config
// volume in a workflow step
volumeRefDockerConfig = volumeRef{
Name: "dockerconfig",
Path: "/root/.docker",
}
)
var buildboxVersion value
var goRuntime value
func init() {
v, err := exec.Command("make", "-s", "-C", "build.assets", "print-go-version").Output()
if err != nil {
log.Fatalf("could not get Go version: %v", err)
}
goRuntime = value{raw: string(bytes.TrimSpace(v))}
v, err = exec.Command("make", "-s", "-C", "build.assets", "print-buildbox-version").Output()
if err != nil {
log.Fatalf("could not get buildbox version: %v", err)
}
buildboxVersion = value{raw: string(bytes.TrimSpace(v))}
}
func pushTriggerForBranch(branches ...string) trigger {
t := trigger{
Event: triggerRef{Include: []string{"push"}},
Repo: triggerRef{Include: []string{"gravitational/teleport"}},
}
t.Branch.Include = append(t.Branch.Include, branches...)
return t
}
func cronTrigger(cronJobNames []string) trigger {
return trigger{
Cron: triggerRef{Include: cronJobNames},
Repo: triggerRef{Include: []string{"gravitational/teleport"}},
}
}
func cloneRepoCommands(cloneDirectory, commit string) []string {
return []string{
fmt.Sprintf("mkdir -pv %q", cloneDirectory),
fmt.Sprintf("cd %q", cloneDirectory),
"git init",
"git remote add origin ${DRONE_REMOTE_URL}",
"git fetch origin --tags",
fmt.Sprintf("git checkout -qf %q", commit),
}
}
type buildType struct {
os string
arch string
fips bool
centos7 bool
windowsUnsigned bool
buildConnect bool
}
// Description provides a human-facing description of the artifact, e.g.:
//
// Windows 64-bit (tsh client only)
// Linux ARMv7 (32-bit)
// MacOS Intel .pkg installer
func (b *buildType) Description(packageType string, extraQualifications ...string) string {
var result string
var os string
var arch string
var darwinArch string
var bitness int
var qualifications []string
switch b.os {
case "linux":
os = "Linux"
case "darwin":
os = "MacOS"
case "windows":
os = "Windows"
default:
panic(fmt.Sprintf("unhandled OS: %s", b.os))
}
switch b.arch {
case "arm64":
arch = "ARM64/ARMv8"
darwinArch = "Apple Silicon"
bitness = 64
case "amd64":
darwinArch = "Intel"
bitness = 64
case "arm":
arch = "ARMv7"
fallthrough
case "386":
bitness = 32
default:
panic(fmt.Sprintf("unhandled arch: %s", b.arch))
}
if b.centos7 {
qualifications = append(qualifications, "RHEL/CentOS 7.x compatible")
}
if b.fips {
qualifications = append(qualifications, "FedRAMP/FIPS")
}
qualifications = append(qualifications, extraQualifications...)
result = os
if b.os == "darwin" {
result += fmt.Sprintf(" %s", darwinArch)
} else {
// arch is implicit for Windows/Linux i386/amd64
if arch == "" {
result += fmt.Sprintf(" %d-bit", bitness)
} else {
result += fmt.Sprintf(" %s (%d-bit)", arch, bitness)
}
}
if packageType != "" {
result += fmt.Sprintf(" %s", packageType)
}
if len(qualifications) > 0 {
result += fmt.Sprintf(" (%s)", strings.Join(qualifications, ", "))
}
return result
}
func (b *buildType) hasTeleportConnect() bool {
return (b.os == "darwin" && b.arch == "amd64") ||
(b.os == "linux" && b.arch == "amd64" && !b.centos7 && !b.fips)
}
// dockerService generates a docker:dind service
// It includes the Docker socket volume by default, plus any extra volumes passed in
func dockerService(v ...volumeRef) service {
return service{
Name: "Start Docker",
Image: "docker:dind",
Privileged: true,
Volumes: append(v, volumeRefDocker),
}
}
// Starts a container registry service at `LocalRegistrySocket`
// This can be pushed/pulled to via `docker push/pull <LocalRegistrySocket>:5000/image:tag`
func dockerRegistryService() service {
return service{
Name: LocalRegistryHostname,
Image: "registry:2",
}
}
// releaseMakefileTarget gets the correct Makefile target for a given arch/fips/centos combo
func releaseMakefileTarget(b buildType) string {
makefileTarget := fmt.Sprintf("release-%s", b.arch)
// All x86_64 binaries are built on CentOS 7 now for better glibc compatibility.
if b.centos7 || b.arch == "amd64" {
makefileTarget += "-centos7"
}
if b.fips {
makefileTarget += "-fips"
}
// Override Windows targets.
if b.os == "windows" {
if b.windowsUnsigned {
makefileTarget = "release-windows-unsigned"
} else {
makefileTarget = "release-windows"
}
}
return makefileTarget
}
// waitForDockerStep returns a step which checks that the Docker socket is active before trying
// to run container operations
func waitForDockerStep() step {
return step{
Name: "Wait for docker",
Image: "docker",
Pull: "if-not-exists",
Commands: []string{
`timeout 30s /bin/sh -c 'while [ ! -S /var/run/docker.sock ]; do sleep 1; done'`,
`printenv DOCKERHUB_PASSWORD | docker login -u="$DOCKERHUB_USERNAME" --password-stdin`,
},
Volumes: []volumeRef{volumeRefDocker, volumeRefDockerConfig},
Environment: map[string]value{
"DOCKERHUB_USERNAME": {fromSecret: "DOCKERHUB_USERNAME"},
"DOCKERHUB_PASSWORD": {fromSecret: "DOCKERHUB_READONLY_TOKEN"},
},
}
}
// waitForDockerStep returns a step which checks that the Docker registry is ready
func waitForDockerRegistryStep() step {
return step{
Name: "Wait for docker registry",
Image: "alpine",
Pull: "if-not-exists",
Commands: []string{
"apk add curl",
fmt.Sprintf(`timeout 30s /bin/sh -c 'while [ "$(curl -s -o /dev/null -w %%{http_code} http://%s/)" != "200" ]; do sleep 1; done'`, LocalRegistrySocket),
},
}
}
func verifyTaggedStep() step {
return step{
Name: "Verify build is tagged",
Image: "alpine:latest",
Pull: "if-not-exists",
Commands: []string{
"[ -n ${DRONE_TAG} ] || (echo 'DRONE_TAG is not set. Is the commit tagged?' && exit 1)",
},
}
}
// Note that tags are also valid here as a tag refers to a specific commit
func cloneRepoStep(clonePath, commit string) step {
return step{
Name: "Check out code",
Image: "alpine/git:latest",
Pull: "if-not-exists",
Commands: cloneRepoCommands(clonePath, commit),
}
}
func sliceSelect[T, V any](slice []T, selector func(T) V) []V {
selectedValues := make([]V, len(slice))
for i, entry := range slice {
selectedValues[i] = selector(entry)
}
return selectedValues
}
func getStepNames(steps []step) []string {
return sliceSelect(steps, func(s step) string { return s.Name })
}