mirror of
https://github.com/gravitational/teleport
synced 2024-10-19 16:53:57 +00:00
dronegen: drone config generator (#6071)
This commit is contained in:
parent
8739417729
commit
026d3419c2
4259
.drone.yml
4259
.drone.yml
File diff suppressed because it is too large
Load diff
22
Makefile
22
Makefile
|
@ -176,6 +176,23 @@ else
|
|||
$(MAKE) --no-print-directory release-unix
|
||||
endif
|
||||
|
||||
# These are aliases used to make build commands uniform.
|
||||
.PHONY: release-amd64
|
||||
release-amd64:
|
||||
$(MAKE) release ARCH=amd64
|
||||
|
||||
.PHONY: release-386
|
||||
release-386:
|
||||
$(MAKE) release ARCH=386
|
||||
|
||||
.PHONY: release-arm
|
||||
release-arm:
|
||||
$(MAKE) release ARCH=arm
|
||||
|
||||
.PHONY: release-arm64
|
||||
release-arm64:
|
||||
$(MAKE) release ARCH=arm64
|
||||
|
||||
#
|
||||
# make release-unix - Produces a binary release tarball containing teleport,
|
||||
# tctl, and tsh.
|
||||
|
@ -604,3 +621,8 @@ update-webassets: WEBAPPS_BRANCH ?= 'master'
|
|||
update-webassets: TELEPORT_BRANCH ?= 'master'
|
||||
update-webassets:
|
||||
build.assets/webapps/update-teleport-webassets.sh -w $(WEBAPPS_BRANCH) -t $(TELEPORT_BRANCH)
|
||||
|
||||
# dronegen generates .drone.yml config
|
||||
.PHONY: dronegen
|
||||
dronegen:
|
||||
go run ./dronegen
|
||||
|
|
|
@ -15,8 +15,8 @@ OS ?= linux
|
|||
ARCH ?= amd64
|
||||
RUNTIME ?= go1.15.5
|
||||
|
||||
UID ?= $$(id -u)
|
||||
GID ?= $$(id -g)
|
||||
UID := $$(id -u)
|
||||
GID := $$(id -g)
|
||||
|
||||
PROTOC_VER ?= 3.6.1
|
||||
PROTOC_PLATFORM := linux-x86_64
|
||||
|
@ -29,6 +29,12 @@ BUILDBOX_CENTOS6_FIPS=quay.io/gravitational/teleport-buildbox-centos6-fips:$(RUN
|
|||
BUILDBOX_ARM=quay.io/gravitational/teleport-buildbox-arm:$(RUNTIME)
|
||||
BUILDBOX_ARM_FIPS=quay.io/gravitational/teleport-buildbox-arm-fips:$(RUNTIME)
|
||||
|
||||
# These variables are used to dynamically change the name of the buildbox Docker image used by the 'release'
|
||||
# target. The other solution was to remove the 'buildbox' dependency from the 'release' target, but this would
|
||||
# make it harder to run `make -C build.assets release` locally as the buildbox would not automatically be built.
|
||||
BUILDBOX_NAME=$(BUILDBOX)
|
||||
BUILDBOX_FIPS_NAME=$(BUILDBOX_FIPS)
|
||||
|
||||
DOCSBOX=quay.io/gravitational/next:main
|
||||
|
||||
ifneq ("$(KUBECONFIG)","")
|
||||
|
@ -84,33 +90,40 @@ build-binaries-fips: buildbox-fips
|
|||
#
|
||||
.PHONY:buildbox
|
||||
buildbox:
|
||||
docker build \
|
||||
--build-arg UID=$(UID) \
|
||||
--build-arg GID=$(GID) \
|
||||
--build-arg RUNTIME=$(RUNTIME) \
|
||||
--build-arg PROTOC_VER=$(PROTOC_VER) \
|
||||
--build-arg GOGO_PROTO_TAG=$(GOGO_PROTO_TAG) \
|
||||
--build-arg PROTOC_PLATFORM=$(PROTOC_PLATFORM) \
|
||||
--cache-from $(BUILDBOX) \
|
||||
--tag $(BUILDBOX) .
|
||||
if [[ "$(BUILDBOX_NAME)" == "$(BUILDBOX)" ]]; then \
|
||||
if [[ $${DRONE} == "true" ]]; then docker pull $(BUILDBOX); fi; \
|
||||
docker build \
|
||||
--build-arg UID=$(UID) \
|
||||
--build-arg GID=$(GID) \
|
||||
--build-arg RUNTIME=$(RUNTIME) \
|
||||
--build-arg PROTOC_VER=$(PROTOC_VER) \
|
||||
--build-arg GOGO_PROTO_TAG=$(GOGO_PROTO_TAG) \
|
||||
--build-arg PROTOC_PLATFORM=$(PROTOC_PLATFORM) \
|
||||
--cache-from $(BUILDBOX) \
|
||||
--tag $(BUILDBOX) . ; \
|
||||
fi
|
||||
|
||||
#
|
||||
# Builds a Docker buildbox for FIPS
|
||||
#
|
||||
.PHONY:buildbox-fips
|
||||
buildbox-fips:
|
||||
docker build \
|
||||
--build-arg UID=$(UID) \
|
||||
--build-arg GID=$(GID) \
|
||||
--build-arg RUNTIME=$(RUNTIME) \
|
||||
--cache-from $(BUILDBOX_FIPS) \
|
||||
--tag $(BUILDBOX_FIPS) -f Dockerfile-fips .
|
||||
if [[ "$(BUILDBOX_FIPS_NAME)" == "$(BUILDBOX_FIPS)" ]]; then \
|
||||
if [[ $${DRONE} == "true" ]]; then docker pull $(BUILDBOX_FIPS); fi; \
|
||||
docker build \
|
||||
--build-arg UID=$(UID) \
|
||||
--build-arg GID=$(GID) \
|
||||
--build-arg RUNTIME=$(RUNTIME) \
|
||||
--cache-from $(BUILDBOX_FIPS) \
|
||||
--tag $(BUILDBOX_FIPS) -f Dockerfile-fips . ; \
|
||||
fi
|
||||
|
||||
#
|
||||
# Builds a Docker buildbox for CentOS 6 builds
|
||||
#
|
||||
.PHONY:buildbox-centos6
|
||||
buildbox-centos6:
|
||||
@if [[ $${DRONE} == "true" ]]; then docker pull $(BUILDBOX_CENTOS6); fi;
|
||||
docker build \
|
||||
--build-arg UID=$(UID) \
|
||||
--build-arg GID=$(GID) \
|
||||
|
@ -123,6 +136,7 @@ buildbox-centos6:
|
|||
#
|
||||
.PHONY:buildbox-centos6-fips
|
||||
buildbox-centos6-fips:
|
||||
@if [[ $${DRONE} == "true" ]]; then docker pull $(BUILDBOX_CENTOS6_FIPS); fi;
|
||||
docker build \
|
||||
--build-arg UID=$(UID) \
|
||||
--build-arg GID=$(GID) \
|
||||
|
@ -137,6 +151,7 @@ buildbox-centos6-fips:
|
|||
#
|
||||
.PHONY:buildbox-arm
|
||||
buildbox-arm: buildbox
|
||||
@if [[ $${DRONE} == "true" ]]; then docker pull $(BUILDBOX_ARM); fi;
|
||||
docker build \
|
||||
--build-arg RUNTIME=$(RUNTIME) \
|
||||
--cache-from $(BUILDBOX) \
|
||||
|
@ -150,6 +165,7 @@ buildbox-arm: buildbox
|
|||
#
|
||||
.PHONY:buildbox-arm-fips
|
||||
buildbox-arm-fips: buildbox-fips
|
||||
@if [[ $${DRONE} == "true" ]]; then docker pull $(BUILDBOX_ARM_FIPS); fi;
|
||||
docker build \
|
||||
--build-arg RUNTIME=$(RUNTIME) \
|
||||
--cache-from $(BUILDBOX_FIPS) \
|
||||
|
@ -218,20 +234,55 @@ enter: buildbox
|
|||
|
||||
#
|
||||
# Create a Teleport package using the build container.
|
||||
# Don't use this target directly; call named Makefile targets like release-amd64.
|
||||
#
|
||||
.PHONY:release
|
||||
release: buildbox
|
||||
docker run $(DOCKERFLAGS) $(BCCFLAGS) -i $(NOROOT) $(BUILDBOX) \
|
||||
docker run $(DOCKERFLAGS) $(BCCFLAGS) -i $(NOROOT) $(BUILDBOX_NAME) \
|
||||
/usr/bin/make release -e ADDFLAGS="$(ADDFLAGS)" OS=$(OS) ARCH=$(ARCH) RUNTIME=$(RUNTIME)
|
||||
|
||||
# These are aliases used to make build commands uniform.
|
||||
.PHONY: release-amd64
|
||||
release-amd64:
|
||||
$(MAKE) release ARCH=amd64
|
||||
|
||||
.PHONY: release-386
|
||||
release-386:
|
||||
$(MAKE) release ARCH=386
|
||||
|
||||
.PHONY: release-arm
|
||||
release-arm: buildbox-arm
|
||||
$(MAKE) release ARCH=arm BUILDBOX_NAME=$(BUILDBOX_ARM)
|
||||
|
||||
.PHONY: release-arm64
|
||||
release-arm64: buildbox-arm
|
||||
$(MAKE) release ARCH=arm64 BUILDBOX_NAME=$(BUILDBOX_ARM)
|
||||
|
||||
.PHONY: release-amd64-fips
|
||||
release-amd64-fips:
|
||||
$(MAKE) release-fips ARCH=amd64 FIPS=yes BUILDBOX_FIPS_NAME=$(BUILDBOX_FIPS)
|
||||
|
||||
.PHONY: release-arm64-fips
|
||||
release-arm64-fips: buildbox-arm-fips
|
||||
$(MAKE) release-fips ARCH=arm64 FIPS=yes BUILDBOX_FIPS_NAME=$(BUILDBOX_ARM_FIPS)
|
||||
|
||||
.PHONY: release-amd64-centos6
|
||||
release-amd64-centos6: buildbox-centos6
|
||||
$(MAKE) release-centos6 ARCH=amd64
|
||||
|
||||
.PHONY: release-amd64-centos6-fips
|
||||
release-amd64-centos6-fips: buildbox-centos6-fips
|
||||
$(MAKE) release-centos6-fips ARCH=amd64 FIPS=yes
|
||||
|
||||
#
|
||||
# Create a Teleport FIPS package using the build container.
|
||||
# This is a special case because it only builds and packages the Enterprise FIPS binaries, no OSS.
|
||||
# CI should not use this target, it should use named Makefile targets like release-amd64-fips.
|
||||
#
|
||||
.PHONY:release-fips
|
||||
release-fips: buildbox-fips
|
||||
@if [ -z ${VERSION} ]; then echo "VERSION is not set"; exit 1; fi
|
||||
docker run $(DOCKERFLAGS) $(BCCFLAGS) -i $(NOROOT) $(BUILDBOX_FIPS) \
|
||||
docker run $(DOCKERFLAGS) $(BCCFLAGS) -i $(NOROOT) $(BUILDBOX_FIPS_NAME) \
|
||||
/usr/bin/make -C e release -e ADDFLAGS="$(ADDFLAGS)" OS=$(OS) ARCH=$(ARCH) RUNTIME=$(RUNTIME) FIPS=yes VERSION=$(VERSION) GITTAG=v$(VERSION)
|
||||
|
||||
#
|
||||
|
@ -259,45 +310,6 @@ release-windows: buildbox
|
|||
docker run $(DOCKERFLAGS) -i $(NOROOT) $(BUILDBOX) \
|
||||
/usr/bin/make release -e ADDFLAGS="$(ADDFLAGS)" OS=windows
|
||||
|
||||
#
|
||||
# Create an ARM Teleport package using the build container.
|
||||
#
|
||||
.PHONY:release-arm
|
||||
release-arm: ARCH=arm
|
||||
release-arm: buildbox-arm
|
||||
docker run $(DOCKERFLAGS) $(BCCFLAGS) -i $(NOROOT) $(BUILDBOX_ARM) \
|
||||
/usr/bin/make release -e ADDFLAGS="$(ADDFLAGS)" OS=$(OS) ARCH=$(ARCH) RUNTIME=$(RUNTIME)
|
||||
|
||||
#
|
||||
# Create an ARM64 Teleport package using the build container.
|
||||
#
|
||||
.PHONY:release-arm64
|
||||
release-arm64: ARCH=arm64
|
||||
release-arm64: buildbox-arm
|
||||
docker run $(DOCKERFLAGS) $(BCCFLAGS) -i $(NOROOT) $(BUILDBOX_ARM) \
|
||||
/usr/bin/make release -e ADDFLAGS="$(ADDFLAGS)" OS=$(OS) ARCH=$(ARCH) RUNTIME=$(RUNTIME)
|
||||
|
||||
#
|
||||
# Create an ARM FIPS Teleport package using the build container.
|
||||
#
|
||||
.PHONY:release-arm-fips
|
||||
release-arm-fips: ARCH=arm
|
||||
release-arm-fips: buildbox-arm-fips
|
||||
@if [ -z ${VERSION} ]; then echo "VERSION is not set"; exit 1; fi
|
||||
docker run $(DOCKERFLAGS) $(BCCFLAGS) -i $(NOROOT) $(BUILDBOX_ARM_FIPS) \
|
||||
/usr/bin/make -C e release -e ADDFLAGS="$(ADDFLAGS)" OS=$(OS) ARCH=$(ARCH) RUNTIME=$(RUNTIME) FIPS=yes VERSION=$(VERSION) GITTAG=v$(VERSION)
|
||||
|
||||
|
||||
#
|
||||
# Create an ARM64 FIPS Teleport package using the build container.
|
||||
#
|
||||
.PHONY:release-arm64-fips
|
||||
release-arm64-fips: ARCH=arm64
|
||||
release-arm64-fips: buildbox-arm-fips
|
||||
@if [ -z ${VERSION} ]; then echo "VERSION is not set"; exit 1; fi
|
||||
docker run $(DOCKERFLAGS) $(BCCFLAGS) -i $(NOROOT) $(BUILDBOX_ARM_FIPS) \
|
||||
/usr/bin/make -C e release -e ADDFLAGS="$(ADDFLAGS)" OS=$(OS) ARCH=$(ARCH) RUNTIME=$(RUNTIME) FIPS=yes VERSION=$(VERSION) GITTAG=v$(VERSION)
|
||||
|
||||
#
|
||||
# Run docs tester to detect problems.
|
||||
#
|
||||
|
|
100
dronegen/common.go
Normal file
100
dronegen/common.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var (
|
||||
triggerPullRequest = trigger{
|
||||
Event: triggerRef{Include: []string{"pull_request"}},
|
||||
Repo: triggerRef{Include: []string{"gravitational/*"}},
|
||||
}
|
||||
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/*"}},
|
||||
}
|
||||
|
||||
volumeDocker = volume{
|
||||
Name: "dockersock",
|
||||
Temp: &volumeTemp{},
|
||||
}
|
||||
volumeTmpfs = volume{
|
||||
Name: "tmpfs",
|
||||
Temp: &volumeTemp{Medium: "memory"},
|
||||
}
|
||||
volumeTmpDind = volume{
|
||||
Name: "tmp-dind",
|
||||
Temp: &volumeTemp{},
|
||||
}
|
||||
volumeTmpIntegration = volume{
|
||||
Name: "tmp-integration",
|
||||
Temp: &volumeTemp{},
|
||||
}
|
||||
|
||||
volumeRefTmpfs = volumeRef{
|
||||
Name: "tmpfs",
|
||||
Path: "/tmpfs",
|
||||
}
|
||||
volumeRefDocker = volumeRef{
|
||||
Name: "dockersock",
|
||||
Path: "/var/run",
|
||||
}
|
||||
volumeRefTmpDind = volumeRef{
|
||||
Name: "tmp-dind",
|
||||
Path: "/tmp",
|
||||
}
|
||||
volumeRefTmpIntegration = volumeRef{
|
||||
Name: "tmp-integration",
|
||||
Path: "/tmp",
|
||||
}
|
||||
|
||||
// TODO(gus): Set this from `make -C build.assets print-runtime-version` or similar rather
|
||||
// than hardcoding it. Also remove the usage of RUNTIME as a pipeline-level environment variable
|
||||
// (as support for these varies among Drone runners) and only set it for steps that need it.
|
||||
goRuntime = value{raw: "go1.15.5"}
|
||||
)
|
||||
|
||||
type buildType struct {
|
||||
os string
|
||||
arch string
|
||||
fips bool
|
||||
centos6 bool
|
||||
}
|
||||
|
||||
// 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",
|
||||
Volumes: append(v, volumeRefDocker),
|
||||
}
|
||||
}
|
||||
|
||||
// dockerVolumes returns a slice of volumes
|
||||
// It includes the Docker socket volume by default, plus any extra volumes passed in
|
||||
func dockerVolumes(v ...volume) []volume {
|
||||
return append(v, volumeDocker)
|
||||
}
|
||||
|
||||
// dockerVolumeRefs returns a slice of volumeRefs
|
||||
// It includes the Docker socket volumeRef as a default, plus any extra volumeRefs passed in
|
||||
func dockerVolumeRefs(v ...volumeRef) []volumeRef {
|
||||
return append(v, volumeRefDocker)
|
||||
}
|
||||
|
||||
// releaseMakefileTarget gets the correct Makefile target for a given arch/fips/centos6 combo
|
||||
func releaseMakefileTarget(b buildType) string {
|
||||
makefileTarget := fmt.Sprintf("release-%s", b.arch)
|
||||
if b.centos6 {
|
||||
makefileTarget += "-centos6"
|
||||
}
|
||||
if b.fips {
|
||||
makefileTarget += "-fips"
|
||||
}
|
||||
return makefileTarget
|
||||
}
|
6
dronegen/cron.go
Normal file
6
dronegen/cron.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package main
|
||||
|
||||
func cronPipelines() []pipeline {
|
||||
// TODO: migrate
|
||||
return nil
|
||||
}
|
28
dronegen/drone_cli.go
Normal file
28
dronegen/drone_cli.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func checkDroneCLI() error {
|
||||
if _, err := exec.LookPath("drone"); err != nil {
|
||||
return fmt.Errorf("can't find drone CLI in $PATH: %w; get it from https://docs.drone.io/cli/install/", err)
|
||||
}
|
||||
if os.Getenv("DRONE_SERVER") == "" || os.Getenv("DRONE_TOKEN") == "" {
|
||||
return fmt.Errorf("$DRONE_SERVER and/or $DRONE_TOKEN env vars not set; get them at https://drone.gravitational.io/account")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func signDroneConfig() error {
|
||||
out, err := exec.Command("drone", "sign", "gravitational/teleport", "--save").CombinedOutput()
|
||||
if err != nil {
|
||||
if len(out) > 0 {
|
||||
err = fmt.Errorf("drone signing failed: %v\noutput:\n%s", err, out)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
122
dronegen/main.go
Normal file
122
dronegen/main.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := checkDroneCLI(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
var pipelines []pipeline
|
||||
|
||||
pipelines = append(pipelines, testPipelines()...)
|
||||
pipelines = append(pipelines, pushPipelines()...)
|
||||
pipelines = append(pipelines, tagPipelines()...)
|
||||
pipelines = append(pipelines, cronPipelines()...)
|
||||
pipelines = append(pipelines, promoteBuildPipeline())
|
||||
pipelines = append(pipelines, updateDocsPipeline())
|
||||
|
||||
if err := writePipelines(".drone.yml", pipelines); err != nil {
|
||||
fmt.Println("failed writing drone pipelines:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := signDroneConfig(); err != nil {
|
||||
fmt.Println("failed signing .drone.yml:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func writePipelines(path string, newPipelines []pipeline) error {
|
||||
// Read the existing config and replace only those pipelines defined in
|
||||
// newPipelines.
|
||||
//
|
||||
// TODO: When all pipelines are migrated, remove this merging logic and
|
||||
// write the file directly. This will be simpler and allow cleanup of
|
||||
// pipelines when they are removed from this generator.
|
||||
existingConfig, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read existing config: %w", err)
|
||||
}
|
||||
existingPipelines, err := parsePipelines(existingConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse existing config: %w", err)
|
||||
}
|
||||
|
||||
newPipelinesSet := make(map[string]pipeline, len(newPipelines))
|
||||
for _, p := range newPipelines {
|
||||
// TODO: remove this check once promoteBuildPipeline and
|
||||
// updateDocsPipeline are implemented.
|
||||
if p.Name == "" {
|
||||
continue
|
||||
}
|
||||
newPipelinesSet[p.Name] = p
|
||||
}
|
||||
|
||||
pipelines := existingPipelines
|
||||
// Overwrite all existing pipelines with new ones that have the same name.
|
||||
for i, p := range pipelines {
|
||||
if np, ok := newPipelinesSet[p.Name]; ok {
|
||||
out, err := yaml.Marshal(np)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encode pipelines: %w", err)
|
||||
}
|
||||
// Add a little note about this being generated.
|
||||
out = append([]byte(np.comment), out...)
|
||||
pipelines[i] = parsedPipeline{pipeline: np, raw: out}
|
||||
delete(newPipelinesSet, np.Name)
|
||||
}
|
||||
}
|
||||
// If we decide to add new pipelines before everything is migrated to this
|
||||
// generator, this check needs to change.
|
||||
if len(newPipelinesSet) != 0 {
|
||||
var names []string
|
||||
for n := range newPipelinesSet {
|
||||
names = append(names, n)
|
||||
}
|
||||
return fmt.Errorf("pipelines %q don't exist in the current config, aborting", names)
|
||||
}
|
||||
|
||||
var pipelinesEnc [][]byte
|
||||
for _, p := range pipelines {
|
||||
pipelinesEnc = append(pipelinesEnc, p.raw)
|
||||
}
|
||||
configData := bytes.Join(pipelinesEnc, []byte("\n---\n"))
|
||||
|
||||
return ioutil.WriteFile(path, configData, 0664)
|
||||
}
|
||||
|
||||
// parsedPipeline is a single pipeline parsed from .drone.yml along with its
|
||||
// unparsed form. It's used to preserve YAML comments and minimize diffs due to
|
||||
// formatting.
|
||||
//
|
||||
// TODO: remove this when all pipelines are migrated. All comments will be
|
||||
// moved to this generator instead.
|
||||
type parsedPipeline struct {
|
||||
pipeline
|
||||
raw []byte
|
||||
}
|
||||
|
||||
func parsePipelines(data []byte) ([]parsedPipeline, error) {
|
||||
chunks := bytes.Split(data, []byte("\n---\n"))
|
||||
var pipelines []parsedPipeline
|
||||
for _, c := range chunks {
|
||||
// Discard the signature, it will be re-generated.
|
||||
if bytes.HasPrefix(c, []byte("kind: signature")) {
|
||||
continue
|
||||
}
|
||||
var p pipeline
|
||||
if err := yaml.UnmarshalStrict(c, &p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pipelines = append(pipelines, parsedPipeline{pipeline: p, raw: c})
|
||||
}
|
||||
return pipelines, nil
|
||||
}
|
11
dronegen/misc.go
Normal file
11
dronegen/misc.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package main
|
||||
|
||||
func promoteBuildPipeline() pipeline {
|
||||
// TODO: migrate
|
||||
return pipeline{}
|
||||
}
|
||||
|
||||
func updateDocsPipeline() pipeline {
|
||||
// TODO: migrate
|
||||
return pipeline{}
|
||||
}
|
134
dronegen/push.go
Normal file
134
dronegen/push.go
Normal file
|
@ -0,0 +1,134 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// pushCheckoutCommands builds a list of commands for Drone to check out a git commit on a push build
|
||||
func pushCheckoutCommands(fips bool) []string {
|
||||
commands := []string{
|
||||
`mkdir -p /go/src/github.com/gravitational/teleport /go/cache`,
|
||||
`cd /go/src/github.com/gravitational/teleport`,
|
||||
`git init && git remote add origin ${DRONE_REMOTE_URL}`,
|
||||
`git fetch origin`,
|
||||
`git checkout -qf ${DRONE_COMMIT_SHA}`,
|
||||
// this is allowed to fail because pre-4.3 Teleport versions don't use the webassets submodule
|
||||
`git submodule update --init webassets || true`,
|
||||
`mkdir -m 0700 /root/.ssh && echo "$GITHUB_PRIVATE_KEY" > /root/.ssh/id_rsa && chmod 600 /root/.ssh/id_rsa`,
|
||||
`ssh-keyscan -H github.com > /root/.ssh/known_hosts 2>/dev/null && chmod 600 /root/.ssh/known_hosts`,
|
||||
`git submodule update --init e`,
|
||||
// do a recursive submodule checkout to get both webassets and webassets/e
|
||||
// this is allowed to fail because pre-4.3 Teleport versions don't use the webassets submodule
|
||||
`git submodule update --init --recursive webassets || true`,
|
||||
`rm -f /root/.ssh/id_rsa`,
|
||||
}
|
||||
if fips {
|
||||
commands = append(commands, `if [[ "${DRONE_TAG}" != "" ]]; then echo "${DRONE_TAG##v}" > /go/.version.txt; else egrep ^VERSION Makefile | cut -d= -f2 > /go/.version.txt; fi; cat /go/.version.txt`)
|
||||
}
|
||||
return commands
|
||||
}
|
||||
|
||||
// pushBuildCommands generates a list of commands for Drone to build an artifact as part of a push build
|
||||
func pushBuildCommands(b buildType) []string {
|
||||
commands := []string{
|
||||
`apk add --no-cache make`,
|
||||
`chown -R $UID:$GID /go`,
|
||||
`cd /go/src/github.com/gravitational/teleport`,
|
||||
}
|
||||
if b.fips {
|
||||
commands = append(commands,
|
||||
`export VERSION=$(cat /go/.version.txt)`,
|
||||
)
|
||||
}
|
||||
commands = append(commands,
|
||||
fmt.Sprintf(`make -C build.assets %s`, releaseMakefileTarget(b)),
|
||||
)
|
||||
return commands
|
||||
}
|
||||
|
||||
// pushPipelines builds all applicable push pipeline combinations
|
||||
func pushPipelines() []pipeline {
|
||||
var ps []pipeline
|
||||
for _, arch := range []string{"amd64", "386", "arm", "arm64"} {
|
||||
for _, fips := range []bool{false, true} {
|
||||
if (arch == "386" || arch == "arm") && fips {
|
||||
// FIPS mode not supported on i386/ARM
|
||||
continue
|
||||
}
|
||||
ps = append(ps, pushPipeline(buildType{os: "linux", arch: arch, fips: fips}))
|
||||
}
|
||||
}
|
||||
// Only amd64 Windows is supported for now.
|
||||
ps = append(ps, pushPipeline(buildType{os: "windows", arch: "amd64"}))
|
||||
return ps
|
||||
}
|
||||
|
||||
// pushPipeline generates a push pipeline for a given combination of os/arch/FIPS
|
||||
func pushPipeline(b buildType) pipeline {
|
||||
if b.os == "" {
|
||||
panic("b.os must be set")
|
||||
}
|
||||
if b.arch == "" {
|
||||
panic("b.arch must be set")
|
||||
}
|
||||
|
||||
pipelineName := fmt.Sprintf("push-build-%s-%s", b.os, b.arch)
|
||||
pushEnvironment := map[string]value{
|
||||
"UID": value{raw: "1000"},
|
||||
"GID": value{raw: "1000"},
|
||||
"GOPATH": value{raw: "/go"},
|
||||
"OS": value{raw: b.os},
|
||||
"ARCH": value{raw: b.arch},
|
||||
}
|
||||
if b.fips {
|
||||
pipelineName += "-fips"
|
||||
pushEnvironment["FIPS"] = value{raw: "yes"}
|
||||
}
|
||||
|
||||
p := newKubePipeline(pipelineName)
|
||||
p.Environment = map[string]value{
|
||||
"RUNTIME": goRuntime,
|
||||
"UID": value{raw: "1000"},
|
||||
"GID": value{raw: "1000"},
|
||||
}
|
||||
p.Trigger = triggerPush
|
||||
p.Workspace = workspace{Path: "/go"}
|
||||
p.Volumes = dockerVolumes()
|
||||
p.Services = []service{
|
||||
dockerService(),
|
||||
}
|
||||
p.Steps = []step{
|
||||
{
|
||||
Name: "Check out code",
|
||||
Image: "docker:git",
|
||||
Environment: map[string]value{
|
||||
"GITHUB_PRIVATE_KEY": value{fromSecret: "GITHUB_PRIVATE_KEY"},
|
||||
},
|
||||
Commands: pushCheckoutCommands(b.fips),
|
||||
},
|
||||
{
|
||||
Name: "Build artifacts",
|
||||
Image: "docker",
|
||||
Environment: pushEnvironment,
|
||||
Volumes: dockerVolumeRefs(),
|
||||
Commands: pushBuildCommands(b),
|
||||
},
|
||||
{
|
||||
Name: "Send Slack notification",
|
||||
Image: "plugins/slack",
|
||||
Settings: map[string]value{
|
||||
"webhook": value{fromSecret: "SLACK_WEBHOOK_DEV_TELEPORT"},
|
||||
},
|
||||
Template: []string{
|
||||
`*{{#success build.status}}✔{{ else }}✘{{/success}} {{ uppercasefirst build.status }}: Build #{{ build.number }}* (type: ` + "`{{ build.event }}`" + `)
|
||||
` + "`${DRONE_STAGE_NAME}`" + ` artifact build failed.
|
||||
*Warning:* This is a genuine failure to build the Teleport binary from ` + "`{{ build.branch }}`" + ` (likely due to a bad merge or commit) and should be investigated immediately.
|
||||
Commit: <https://github.com/{{ repo.owner }}/{{ repo.name }}/commit/{{ build.commit }}|{{ truncate build.commit 8 }}>
|
||||
Branch: <https://github.com/{{ repo.owner }}/{{ repo.name }}/commits/{{ build.branch }}|{{ repo.owner }}/{{ repo.name }}:{{ build.branch }}>
|
||||
Author: <https://github.com/{{ build.author }}|{{ build.author }}>
|
||||
<{{ build.link }}|Visit Drone build page ↗>
|
||||
`,
|
||||
},
|
||||
When: &condition{Status: []string{"failure"}},
|
||||
},
|
||||
}
|
||||
return p
|
||||
}
|
379
dronegen/tag.go
Normal file
379
dronegen/tag.go
Normal file
|
@ -0,0 +1,379 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
// rpmPackage is the RPM package type
|
||||
rpmPackage = "rpm"
|
||||
// debPackage is the DEB package type
|
||||
debPackage = "deb"
|
||||
)
|
||||
|
||||
// tagCheckoutCommands builds a list of commands for Drone to check out a git commit on a tag build
|
||||
func tagCheckoutCommands(fips bool) []string {
|
||||
commands := []string{
|
||||
`mkdir -p /go/src/github.com/gravitational/teleport`,
|
||||
`cd /go/src/github.com/gravitational/teleport`,
|
||||
`git clone https://github.com/gravitational/${DRONE_REPO_NAME}.git .`,
|
||||
`git checkout ${DRONE_TAG:-$DRONE_COMMIT}`,
|
||||
// fetch enterprise submodules
|
||||
`mkdir -m 0700 /root/.ssh && echo -n "$GITHUB_PRIVATE_KEY" > /root/.ssh/id_rsa && chmod 600 /root/.ssh/id_rsa`,
|
||||
`ssh-keyscan -H github.com > /root/.ssh/known_hosts 2>/dev/null && chmod 600 /root/.ssh/known_hosts`,
|
||||
`git submodule update --init e`,
|
||||
// this is allowed to fail because pre-4.3 Teleport versions don't use the webassets submodule
|
||||
`git submodule update --init --recursive webassets || true`,
|
||||
`rm -f /root/.ssh/id_rsa`,
|
||||
// create necessary directories
|
||||
`mkdir -p /go/cache /go/artifacts`,
|
||||
// set version
|
||||
`if [[ "${DRONE_TAG}" != "" ]]; then echo "${DRONE_TAG##v}" > /go/.version.txt; else egrep ^VERSION Makefile | cut -d= -f2 > /go/.version.txt; fi; cat /go/.version.txt`,
|
||||
}
|
||||
return commands
|
||||
}
|
||||
|
||||
// tagBuildCommands generates a list of commands for Drone to build an artifact as part of a tag build
|
||||
func tagBuildCommands(b buildType) []string {
|
||||
commands := []string{
|
||||
`apk add --no-cache make`,
|
||||
`chown -R $UID:$GID /go`,
|
||||
`cd /go/src/github.com/gravitational/teleport`,
|
||||
}
|
||||
|
||||
if b.fips {
|
||||
commands = append(commands,
|
||||
"export VERSION=$(cat /go/.version.txt)",
|
||||
)
|
||||
}
|
||||
|
||||
commands = append(commands,
|
||||
fmt.Sprintf(
|
||||
`make -C build.assets %s`, releaseMakefileTarget(b),
|
||||
),
|
||||
)
|
||||
|
||||
return commands
|
||||
}
|
||||
|
||||
// tagCopyArtifactCommands generates a set of commands to find and copy built tarball artifacts as part of a tag build
|
||||
func tagCopyArtifactCommands(b buildType) []string {
|
||||
extension := ".tar.gz"
|
||||
if b.os == "windows" {
|
||||
extension = ".zip"
|
||||
}
|
||||
|
||||
commands := []string{
|
||||
`cd /go/src/github.com/gravitational/teleport`,
|
||||
}
|
||||
|
||||
// don't copy OSS artifacts for any FIPS build
|
||||
if !b.fips {
|
||||
commands = append(commands,
|
||||
fmt.Sprintf(`find . -maxdepth 1 -iname "teleport*%s" -print -exec cp {} /go/artifacts \;`, extension),
|
||||
)
|
||||
}
|
||||
|
||||
// copy enterprise artifacts
|
||||
if b.os == "windows" {
|
||||
commands = append(commands,
|
||||
`export VERSION=$(cat /go/.version.txt)`,
|
||||
`cp /go/artifacts/teleport-v$${VERSION}-windows-amd64-bin.zip /go/artifacts/teleport-ent-v$${VERSION}-windows-amd64-bin.zip`,
|
||||
)
|
||||
} else {
|
||||
commands = append(commands,
|
||||
`find e/ -maxdepth 1 -iname "teleport*.tar.gz" -print -exec cp {} /go/artifacts \;`,
|
||||
)
|
||||
}
|
||||
|
||||
// we need to specifically rename artifacts which are created for CentOS 6
|
||||
// these is the only special case where renaming is not handled inside the Makefile
|
||||
if b.centos6 {
|
||||
commands = append(commands, `export VERSION=$(cat /go/.version.txt)`)
|
||||
if !b.fips {
|
||||
commands = append(commands,
|
||||
`mv /go/artifacts/teleport-v$${VERSION}-linux-amd64-bin.tar.gz /go/artifacts/teleport-v$${VERSION}-linux-amd64-centos6-bin.tar.gz`,
|
||||
`mv /go/artifacts/teleport-ent-v$${VERSION}-linux-amd64-bin.tar.gz /go/artifacts/teleport-ent-v$${VERSION}-linux-amd64-centos6-bin.tar.gz`,
|
||||
)
|
||||
} else {
|
||||
commands = append(commands,
|
||||
`mv /go/artifacts/teleport-ent-v$${VERSION}-linux-amd64-fips-bin.tar.gz /go/artifacts/teleport-ent-v$${VERSION}-linux-amd64-centos6-fips-bin.tar.gz`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// generate checksums
|
||||
commands = append(commands, fmt.Sprintf(`cd /go/artifacts && for FILE in teleport*%s; do sha256sum $FILE > $FILE.sha256; done && ls -l`, extension))
|
||||
return commands
|
||||
}
|
||||
|
||||
type s3Settings struct {
|
||||
region string
|
||||
source string
|
||||
target string
|
||||
stripPrefix string
|
||||
}
|
||||
|
||||
// uploadToS3Step generates an S3 upload step
|
||||
func uploadToS3Step(s s3Settings) step {
|
||||
return step{
|
||||
Name: "Upload to S3",
|
||||
Image: "plugins/s3",
|
||||
Settings: map[string]value{
|
||||
"bucket": value{fromSecret: "AWS_S3_BUCKET"},
|
||||
"access_key": value{fromSecret: "AWS_ACCESS_KEY_ID"},
|
||||
"secret_key": value{fromSecret: "AWS_SECRET_ACCESS_KEY"},
|
||||
"region": value{raw: s.region},
|
||||
"source": value{raw: s.source},
|
||||
"target": value{raw: s.target},
|
||||
"strip_prefix": value{raw: s.stripPrefix},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// tagPipelines builds all applicable tag pipeline combinations
|
||||
func tagPipelines() []pipeline {
|
||||
var ps []pipeline
|
||||
// regular tarball builds
|
||||
for _, arch := range []string{"amd64", "386", "arm", "arm64"} {
|
||||
for _, fips := range []bool{false, true} {
|
||||
if (arch == "386" || arch == "arm") && fips {
|
||||
// FIPS mode not supported on i386 or ARM
|
||||
continue
|
||||
}
|
||||
ps = append(ps, tagPipeline(buildType{os: "linux", arch: arch, fips: fips}))
|
||||
|
||||
if arch == "arm" || arch == "arm64" {
|
||||
// TODO(gus): support needs to be added upstream for building ARM/ARM64 packages first
|
||||
continue
|
||||
}
|
||||
for _, packageType := range []string{rpmPackage, debPackage} {
|
||||
ps = append(ps, tagPackagePipeline(packageType, buildType{os: "linux", arch: arch, fips: fips}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only amd64 Windows is supported for now.
|
||||
ps = append(ps, tagPipeline(buildType{os: "windows", arch: "amd64"}))
|
||||
|
||||
// Also add the two CentOS 6 artifacts.
|
||||
ps = append(ps, tagPipeline(buildType{os: "linux", arch: "amd64", centos6: true}))
|
||||
ps = append(ps, tagPipeline(buildType{os: "linux", arch: "amd64", centos6: true, fips: true}))
|
||||
return ps
|
||||
}
|
||||
|
||||
// tagPipeline generates a tag pipeline for a given combination of os/arch/FIPS
|
||||
func tagPipeline(b buildType) pipeline {
|
||||
if b.os == "" {
|
||||
panic("b.os must be set")
|
||||
}
|
||||
if b.arch == "" {
|
||||
panic("b.arch must be set")
|
||||
}
|
||||
|
||||
pipelineName := fmt.Sprintf("build-%s-%s", b.os, b.arch)
|
||||
if b.centos6 {
|
||||
pipelineName += "-centos6"
|
||||
}
|
||||
tagEnvironment := map[string]value{
|
||||
"UID": value{raw: "1000"},
|
||||
"GID": value{raw: "1000"},
|
||||
"GOPATH": value{raw: "/go"},
|
||||
"OS": value{raw: b.os},
|
||||
"ARCH": value{raw: b.arch},
|
||||
}
|
||||
if b.fips {
|
||||
pipelineName += "-fips"
|
||||
tagEnvironment["FIPS"] = value{raw: "yes"}
|
||||
}
|
||||
|
||||
p := newKubePipeline(pipelineName)
|
||||
p.Environment = map[string]value{
|
||||
"RUNTIME": goRuntime,
|
||||
}
|
||||
p.Trigger = triggerTag
|
||||
p.Workspace = workspace{Path: "/go"}
|
||||
p.Volumes = dockerVolumes()
|
||||
p.Services = []service{
|
||||
dockerService(),
|
||||
}
|
||||
p.Steps = []step{
|
||||
{
|
||||
Name: "Check out code",
|
||||
Image: "docker:git",
|
||||
Environment: map[string]value{
|
||||
"GITHUB_PRIVATE_KEY": value{fromSecret: "GITHUB_PRIVATE_KEY"},
|
||||
},
|
||||
Commands: tagCheckoutCommands(b.fips),
|
||||
},
|
||||
{
|
||||
Name: "Build artifacts",
|
||||
Image: "docker",
|
||||
Environment: tagEnvironment,
|
||||
Volumes: dockerVolumeRefs(),
|
||||
Commands: tagBuildCommands(b),
|
||||
},
|
||||
{
|
||||
Name: "Copy artifacts",
|
||||
Image: "docker",
|
||||
Commands: tagCopyArtifactCommands(b),
|
||||
},
|
||||
uploadToS3Step(s3Settings{
|
||||
region: "us-west-2",
|
||||
source: "/go/artifacts/*",
|
||||
target: "teleport/tag/${DRONE_TAG##v}",
|
||||
stripPrefix: "/go/artifacts/",
|
||||
}),
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// tagDownloadArtifactCommands generates a set of commands to download appropriate artifacts for creating a package as part of a tag build
|
||||
func tagDownloadArtifactCommands(b buildType) []string {
|
||||
commands := []string{
|
||||
`export VERSION=$(cat /go/.version.txt)`,
|
||||
`if [[ "${DRONE_TAG}" != "" ]]; then export S3_PATH="tag/$${DRONE_TAG##v}/"; else export S3_PATH="tag/"; fi`,
|
||||
}
|
||||
artifactOSS := true
|
||||
artifactType := fmt.Sprintf("%s-%s", b.os, b.arch)
|
||||
if b.fips {
|
||||
artifactType += "-fips"
|
||||
artifactOSS = false
|
||||
}
|
||||
|
||||
if artifactOSS {
|
||||
commands = append(commands,
|
||||
fmt.Sprintf(`aws s3 cp s3://$AWS_S3_BUCKET/teleport/$${S3_PATH}teleport-v$${VERSION}-%s-bin.tar.gz /go/artifacts/`, artifactType),
|
||||
)
|
||||
}
|
||||
commands = append(commands,
|
||||
fmt.Sprintf(`aws s3 cp s3://$AWS_S3_BUCKET/teleport/$${S3_PATH}teleport-ent-v$${VERSION}-%s-bin.tar.gz /go/artifacts/`, artifactType),
|
||||
)
|
||||
return commands
|
||||
}
|
||||
|
||||
// tagCopyPackageArtifactCommands generates a set of commands to find and copy built package artifacts as part of a tag build
|
||||
func tagCopyPackageArtifactCommands(b buildType, packageType string) []string {
|
||||
commands := []string{
|
||||
`cd /go/src/github.com/gravitational/teleport`,
|
||||
}
|
||||
if !b.fips {
|
||||
commands = append(commands, fmt.Sprintf(`find build -maxdepth 1 -iname "teleport*.%s*" -print -exec cp {} /go/artifacts \;`, packageType))
|
||||
}
|
||||
commands = append(commands, fmt.Sprintf(`find e/build -maxdepth 1 -iname "teleport*.%s*" -print -exec cp {} /go/artifacts \;`, packageType))
|
||||
return commands
|
||||
}
|
||||
|
||||
// tagPackagePipeline generates a tag package pipeline for a given combination of os/arch/FIPS
|
||||
func tagPackagePipeline(packageType string, b buildType) pipeline {
|
||||
if packageType == "" {
|
||||
panic("packageType must be set")
|
||||
}
|
||||
if b.os == "" {
|
||||
panic("b.os must be set")
|
||||
}
|
||||
if b.arch == "" {
|
||||
panic("b.arch must be set")
|
||||
}
|
||||
|
||||
environment := map[string]value{
|
||||
"ARCH": value{raw: b.arch},
|
||||
"TMPDIR": value{raw: "/go"},
|
||||
"ENT_TARBALL_PATH": value{raw: "/go/artifacts"},
|
||||
}
|
||||
|
||||
dependentPipeline := fmt.Sprintf("build-%s-%s", b.os, b.arch)
|
||||
packageBuildCommands := []string{
|
||||
`apk add --no-cache bash curl gzip make tar`,
|
||||
`cd /go/src/github.com/gravitational/teleport`,
|
||||
`export VERSION=$(cat /go/.version.txt)`,
|
||||
}
|
||||
|
||||
makeCommand := fmt.Sprintf("make %s", packageType)
|
||||
if b.fips {
|
||||
dependentPipeline += "-fips"
|
||||
environment["FIPS"] = value{raw: "yes"}
|
||||
environment["RUNTIME"] = value{raw: "fips"}
|
||||
makeCommand = fmt.Sprintf("make -C e %s", packageType)
|
||||
} else {
|
||||
environment["OSS_TARBALL_PATH"] = value{raw: "/go/artifacts"}
|
||||
}
|
||||
|
||||
packageDockerVolumes := dockerVolumes()
|
||||
packageDockerVolumeRefs := dockerVolumeRefs()
|
||||
packageDockerService := dockerService()
|
||||
|
||||
switch packageType {
|
||||
case rpmPackage:
|
||||
environment["GNUPG_DIR"] = value{raw: "/tmpfs/gnupg"}
|
||||
environment["GPG_RPM_SIGNING_ARCHIVE"] = value{fromSecret: "GPG_RPM_SIGNING_ARCHIVE"}
|
||||
packageBuildCommands = append(packageBuildCommands,
|
||||
`mkdir -m0700 $GNUPG_DIR`,
|
||||
`echo "$GPG_RPM_SIGNING_ARCHIVE" | base64 -d | tar -xzf - -C $GNUPG_DIR`,
|
||||
`chown -R root:root $GNUPG_DIR`,
|
||||
makeCommand,
|
||||
`rm -rf $GNUPG_DIR`,
|
||||
)
|
||||
// RPM builds require tmpfs to hold the key material in memory.
|
||||
packageDockerVolumes = dockerVolumes(volumeTmpfs)
|
||||
packageDockerVolumeRefs = dockerVolumeRefs(volumeRefTmpfs)
|
||||
packageDockerService = dockerService(volumeRefTmpfs)
|
||||
case debPackage:
|
||||
packageBuildCommands = append(packageBuildCommands,
|
||||
makeCommand,
|
||||
)
|
||||
default:
|
||||
panic("packageType is not set")
|
||||
}
|
||||
|
||||
pipelineName := fmt.Sprintf("%s-%s", dependentPipeline, packageType)
|
||||
|
||||
p := newKubePipeline(pipelineName)
|
||||
p.Trigger = triggerTag
|
||||
p.DependsOn = []string{dependentPipeline}
|
||||
p.Workspace = workspace{Path: "/go"}
|
||||
p.Volumes = packageDockerVolumes
|
||||
p.Services = []service{
|
||||
packageDockerService,
|
||||
}
|
||||
p.Steps = []step{
|
||||
{
|
||||
Name: "Check out code",
|
||||
Image: "docker:git",
|
||||
Environment: map[string]value{
|
||||
"GITHUB_PRIVATE_KEY": value{fromSecret: "GITHUB_PRIVATE_KEY"},
|
||||
},
|
||||
Commands: tagCheckoutCommands(b.fips),
|
||||
},
|
||||
{
|
||||
Name: "Download artifacts from S3",
|
||||
Image: "amazon/aws-cli",
|
||||
Environment: map[string]value{
|
||||
"AWS_REGION": value{raw: "us-west-2"},
|
||||
"AWS_S3_BUCKET": value{fromSecret: "AWS_S3_BUCKET"},
|
||||
"AWS_ACCESS_KEY_ID": value{fromSecret: "AWS_ACCESS_KEY_ID"},
|
||||
"AWS_SECRET_ACCESS_KEY": value{fromSecret: "AWS_SECRET_ACCESS_KEY"},
|
||||
},
|
||||
Commands: tagDownloadArtifactCommands(b),
|
||||
},
|
||||
{
|
||||
Name: "Build artifacts",
|
||||
Image: "docker",
|
||||
Environment: environment,
|
||||
Volumes: packageDockerVolumeRefs,
|
||||
Commands: packageBuildCommands,
|
||||
},
|
||||
{
|
||||
Name: "Copy artifacts",
|
||||
Image: "docker",
|
||||
Commands: tagCopyPackageArtifactCommands(b, packageType),
|
||||
},
|
||||
uploadToS3Step(s3Settings{
|
||||
region: "us-west-2",
|
||||
source: "/go/artifacts/*",
|
||||
target: "teleport/tag/${DRONE_TAG##v}",
|
||||
stripPrefix: "/go/artifacts/",
|
||||
}),
|
||||
}
|
||||
return p
|
||||
}
|
248
dronegen/tests.go
Normal file
248
dronegen/tests.go
Normal file
|
@ -0,0 +1,248 @@
|
|||
package main
|
||||
|
||||
func testPipelines() []pipeline {
|
||||
return []pipeline{
|
||||
testCodePipeline(),
|
||||
testDocsPipeline(),
|
||||
}
|
||||
}
|
||||
|
||||
// testCheckoutCommands returns a set of commands for checking out Teleport's code
|
||||
// Setting enterprise to true will also add a check against the Github API to determine whether
|
||||
// the pull request comes from a code fork (and will only check out Enterprise code if it does not)
|
||||
func testCheckoutCommands(enterprise bool) []string {
|
||||
commands := []string{
|
||||
`mkdir -p /tmpfs/go/src/github.com/gravitational/teleport /tmpfs/go/cache`,
|
||||
`cd /tmpfs/go/src/github.com/gravitational/teleport`,
|
||||
`git init && git remote add origin ${DRONE_REMOTE_URL}`,
|
||||
`# handle pull requests
|
||||
if [ "${DRONE_BUILD_EVENT}" = "pull_request" ]; then
|
||||
git fetch origin +refs/heads/${DRONE_COMMIT_BRANCH}:
|
||||
git checkout ${DRONE_COMMIT_BRANCH}
|
||||
git fetch origin ${DRONE_COMMIT_REF}:
|
||||
git merge ${DRONE_COMMIT}
|
||||
# handle tags
|
||||
elif [ "${DRONE_BUILD_EVENT}" = "tag" ]; then
|
||||
git fetch origin +refs/tags/${DRONE_TAG}:
|
||||
git checkout -qf FETCH_HEAD
|
||||
# handle pushes/other events
|
||||
else
|
||||
if [ "${DRONE_COMMIT_BRANCH}" = "" ]; then
|
||||
git fetch origin
|
||||
git checkout -qf ${DRONE_COMMIT_SHA}
|
||||
else
|
||||
git fetch origin +refs/heads/${DRONE_COMMIT_BRANCH}:
|
||||
git checkout ${DRONE_COMMIT} -b ${DRONE_COMMIT_BRANCH}
|
||||
fi
|
||||
fi
|
||||
`,
|
||||
}
|
||||
if enterprise {
|
||||
commands = append(commands,
|
||||
// this is allowed to fail because pre-4.3 Teleport versions
|
||||
// don't use the webassets submodule.
|
||||
`git submodule update --init webassets || true`,
|
||||
// use the Github API to check whether this PR comes from a forked repo or not.
|
||||
// if it does, don't check out the Enterprise code.
|
||||
`if [ "${DRONE_BUILD_EVENT}" = "pull_request" ]; then
|
||||
apk add --no-cache curl jq
|
||||
export PR_REPO=$(curl -Ls https://api.github.com/repos/gravitational/${DRONE_REPO_NAME}/pulls/${DRONE_PULL_REQUEST} | jq -r '.head.repo.full_name')
|
||||
echo "---> Source repo for PR ${DRONE_PULL_REQUEST}: $${PR_REPO}"
|
||||
# if the source repo for the PR matches DRONE_REPO, then this is not a PR raised from a fork
|
||||
if [ "$${PR_REPO}" = "${DRONE_REPO}" ] || [ "${DRONE_REPO}" = "gravitational/teleport-private" ]; then
|
||||
mkdir -m 0700 /root/.ssh && echo -n "$GITHUB_PRIVATE_KEY" > /root/.ssh/id_rsa && chmod 600 /root/.ssh/id_rsa
|
||||
ssh-keyscan -H github.com > /root/.ssh/known_hosts 2>/dev/null && chmod 600 /root/.ssh/known_hosts
|
||||
git submodule update --init e
|
||||
# do a recursive submodule checkout to get both webassets and webassets/e
|
||||
# this is allowed to fail because pre-4.3 Teleport versions don't use the webassets submodule
|
||||
git submodule update --init --recursive webassets || true
|
||||
rm -f /root/.ssh/id_rsa
|
||||
fi
|
||||
fi
|
||||
`,
|
||||
)
|
||||
}
|
||||
return commands
|
||||
}
|
||||
|
||||
// testCodePipeline returns a pipeline which runs the linter plus unit and integration tests
|
||||
func testCodePipeline() pipeline {
|
||||
p := newKubePipeline("test")
|
||||
p.Environment = map[string]value{
|
||||
"RUNTIME": goRuntime,
|
||||
"UID": value{raw: "1000"},
|
||||
"GID": value{raw: "1000"},
|
||||
}
|
||||
p.Trigger = triggerPullRequest
|
||||
p.Workspace = workspace{Path: "/go"}
|
||||
p.Volumes = dockerVolumes(volumeTmpfs, volumeTmpDind, volumeTmpIntegration)
|
||||
p.Services = []service{
|
||||
dockerService(volumeRefTmpfs, volumeRefTmpDind),
|
||||
}
|
||||
goEnvironment := map[string]value{
|
||||
"GOCACHE": value{raw: "/tmpfs/go/cache"},
|
||||
"GOPATH": value{raw: "/tmpfs/go"},
|
||||
}
|
||||
p.Steps = []step{
|
||||
{
|
||||
Name: "Check out code",
|
||||
Image: "docker:git",
|
||||
Environment: map[string]value{
|
||||
"GITHUB_PRIVATE_KEY": value{fromSecret: "GITHUB_PRIVATE_KEY"},
|
||||
},
|
||||
Volumes: []volumeRef{
|
||||
volumeRefTmpfs,
|
||||
},
|
||||
Commands: testCheckoutCommands(true),
|
||||
},
|
||||
{
|
||||
Name: "Build buildbox",
|
||||
Image: "docker",
|
||||
Volumes: dockerVolumeRefs(volumeRefTmpfs),
|
||||
Commands: []string{
|
||||
`apk add --no-cache make`,
|
||||
`chown -R $UID:$GID /tmpfs/go`,
|
||||
`docker pull quay.io/gravitational/teleport-buildbox:$RUNTIME || true`,
|
||||
`cd /tmpfs/go/src/github.com/gravitational/teleport`,
|
||||
`make -C build.assets buildbox`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Run linter",
|
||||
Image: "docker",
|
||||
Environment: goEnvironment,
|
||||
Volumes: dockerVolumeRefs(volumeRefTmpfs),
|
||||
Commands: []string{
|
||||
`apk add --no-cache make`,
|
||||
`chown -R $UID:$GID /tmpfs/go`,
|
||||
`cd /tmpfs/go/src/github.com/gravitational/teleport`,
|
||||
`make -C build.assets lint`,
|
||||
},
|
||||
},
|
||||
{
|
||||
// https://discourse.drone.io/t/how-to-exit-a-pipeline-early-without-failing/3951
|
||||
// this step looks at the output of git diff --raw to determine
|
||||
// whether any files which don't match the pattern '^docs/',
|
||||
// '.mdx$' or '.md$' were changed. if there are no changes to
|
||||
// non-docs code, we skip the Teleport tests and exit early with a
|
||||
// special Drone exit code to speed up iteration on docs (as milv
|
||||
// is much quicker to run)
|
||||
Name: "Optionally skip tests",
|
||||
Image: "docker:git",
|
||||
Volumes: []volumeRef{
|
||||
volumeRefTmpfs,
|
||||
},
|
||||
Commands: []string{
|
||||
`cd /tmpfs/go/src/github.com/gravitational/teleport
|
||||
echo -e "\n---> git diff --raw ${DRONE_COMMIT}..origin/${DRONE_COMMIT_BRANCH:-master}\n"
|
||||
git diff --raw ${DRONE_COMMIT}..origin/${DRONE_COMMIT_BRANCH:-master}
|
||||
git diff --raw ${DRONE_COMMIT}..origin/${DRONE_COMMIT_BRANCH:-master} | awk '{print $6}' | grep -Ev '^docs/' | grep -Ev '.mdx$' | grep -Ev '.md$' | grep -v ^$ | wc -l > /tmp/.change_count.txt
|
||||
export CHANGE_COUNT=$(cat /tmp/.change_count.txt | tr -d '\n')
|
||||
echo -e "\n---> Non-docs changes detected: $$CHANGE_COUNT"
|
||||
if [ $$CHANGE_COUNT -gt 0 ]; then
|
||||
echo "---> Teleport tests will run normally"
|
||||
else
|
||||
echo "---> Skipping Teleport tests and exiting early"
|
||||
exit 78
|
||||
fi
|
||||
echo ""
|
||||
`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Run unit and chaos tests",
|
||||
Image: "docker",
|
||||
Environment: goEnvironment,
|
||||
Volumes: dockerVolumeRefs(volumeRefTmpfs),
|
||||
Commands: []string{
|
||||
`apk add --no-cache make`,
|
||||
`chown -R $UID:$GID /tmpfs/go`,
|
||||
`cd /tmpfs/go/src/github.com/gravitational/teleport`,
|
||||
`make -C build.assets test`,
|
||||
},
|
||||
},
|
||||
{
|
||||
// some integration tests can only be run as root, so we handle
|
||||
// these separately using a build tag
|
||||
Name: "Run root-only integration tests",
|
||||
Image: "docker",
|
||||
Environment: goEnvironment,
|
||||
Volumes: dockerVolumeRefs(volumeRefTmpfs, volumeRefTmpIntegration),
|
||||
Commands: []string{
|
||||
`apk add --no-cache make`,
|
||||
`cd /tmpfs/go/src/github.com/gravitational/teleport`,
|
||||
`make -C build.assets integration-root`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Run integration tests",
|
||||
Image: "docker",
|
||||
Environment: map[string]value{
|
||||
"GOCACHE": value{raw: "/tmpfs/go/cache"},
|
||||
"GOPATH": value{raw: "/tmpfs/go"},
|
||||
"INTEGRATION_CI_KUBECONFIG": value{fromSecret: "INTEGRATION_CI_KUBECONFIG"},
|
||||
"KUBECONFIG": value{raw: "/tmpfs/go/kubeconfig.ci"},
|
||||
"TEST_KUBE": value{raw: "true"},
|
||||
},
|
||||
Volumes: dockerVolumeRefs(volumeRefTmpfs, volumeRefTmpIntegration),
|
||||
Commands: []string{
|
||||
`apk add --no-cache make`,
|
||||
// write kubeconfig to disk for use in kube integration tests
|
||||
`echo "$INTEGRATION_CI_KUBECONFIG" > "$KUBECONFIG"`,
|
||||
`chown -R $UID:$GID /tmpfs/go`,
|
||||
`cd /tmpfs/go/src/github.com/gravitational/teleport`,
|
||||
`make -C build.assets integration`,
|
||||
`rm -f "$KUBECONFIG"`,
|
||||
},
|
||||
},
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func testDocsPipeline() pipeline {
|
||||
p := newKubePipeline("test-docs")
|
||||
p.Trigger = triggerPullRequest
|
||||
p.Workspace = workspace{Path: "/go"}
|
||||
p.Volumes = dockerVolumes(volumeTmpfs)
|
||||
p.Services = []service{
|
||||
dockerService(volumeRefTmpfs),
|
||||
}
|
||||
p.Steps = []step{
|
||||
{
|
||||
Name: "Check out code",
|
||||
Image: "docker:git",
|
||||
Volumes: []volumeRef{
|
||||
volumeRefTmpfs,
|
||||
},
|
||||
Commands: testCheckoutCommands(false),
|
||||
},
|
||||
{
|
||||
Name: "Run docs tests",
|
||||
Image: "docker:git",
|
||||
Environment: map[string]value{
|
||||
"GOCACHE": value{raw: "/tmpfs/go/cache"},
|
||||
"UID": value{raw: "1000"},
|
||||
"GID": value{raw: "1000"},
|
||||
},
|
||||
Volumes: dockerVolumeRefs(volumeRefTmpfs),
|
||||
Commands: []string{
|
||||
`apk add --no-cache make`,
|
||||
`cd /tmpfs/go/src/github.com/gravitational/teleport`,
|
||||
`chown -R $UID:$GID /tmpfs/go`,
|
||||
`git diff --raw ${DRONE_COMMIT}..origin/${DRONE_COMMIT_BRANCH:-master} | awk '{print $6}' | grep -E '^docs' | { grep -v ^$ || true; } > /tmp/docs-changes.txt`,
|
||||
`if [ $(cat /tmp/docs-changes.txt | wc -l) -gt 0 ]; then
|
||||
echo "---> Changes to docs detected"
|
||||
cat /tmp/docs-changes.txt
|
||||
echo "---> Checking for trailing whitespace"
|
||||
make docs-test-whitespace
|
||||
echo "---> Checking for dead links"
|
||||
make -C build.assets test-docs
|
||||
else
|
||||
echo "---> No changes to docs detected, not running tests"
|
||||
fi
|
||||
`,
|
||||
},
|
||||
},
|
||||
}
|
||||
return p
|
||||
}
|
197
dronegen/types.go
Normal file
197
dronegen/types.go
Normal file
|
@ -0,0 +1,197 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Types to mirror the YAML fields of the drone config.
|
||||
// See https://docs.drone.io/pipeline/kubernetes/syntax/ and https://docs.drone.io/pipeline/exec/syntax/.
|
||||
|
||||
type pipeline struct {
|
||||
comment string
|
||||
|
||||
Kind string `yaml:"kind"`
|
||||
Type string `yaml:"type"`
|
||||
Name string `yaml:"name"`
|
||||
Environment map[string]value `yaml:"environment,omitempty"`
|
||||
Trigger trigger `yaml:"trigger"`
|
||||
Workspace workspace `yaml:"workspace,omitempty"`
|
||||
Platform platform `yaml:"platform,omitempty"`
|
||||
Clone clone `yaml:"clone,omitempty"`
|
||||
DependsOn []string `yaml:"depends_on,omitempty"`
|
||||
Concurrency concurrency `yaml:"concurrency,omitempty"`
|
||||
Steps []step `yaml:"steps"`
|
||||
Services []service `yaml:"services,omitempty"`
|
||||
Volumes []volume `yaml:"volumes,omitempty"`
|
||||
}
|
||||
|
||||
func newKubePipeline(name string) pipeline {
|
||||
return pipeline{
|
||||
comment: generatedComment(),
|
||||
Kind: "pipeline",
|
||||
Type: "kubernetes",
|
||||
Name: name,
|
||||
Clone: clone{Disable: true},
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:deadcode,unused
|
||||
func newExecPipeline(name string) pipeline {
|
||||
return pipeline{
|
||||
comment: generatedComment(),
|
||||
Kind: "pipeline",
|
||||
Type: "exec",
|
||||
Name: name,
|
||||
Clone: clone{Disable: true},
|
||||
}
|
||||
}
|
||||
|
||||
func generatedComment() string {
|
||||
c := `################################################
|
||||
# Generated using dronegen, do not edit by hand!
|
||||
# Use 'make dronegen' to update.
|
||||
`
|
||||
_, file, line, ok := runtime.Caller(2)
|
||||
if ok {
|
||||
// Trim off the local path to the repo.
|
||||
i := strings.LastIndex(file, "dronegen")
|
||||
if i > 0 {
|
||||
file = file[i:]
|
||||
}
|
||||
c += fmt.Sprintf("# Generated at %s:%d\n", file, line)
|
||||
}
|
||||
c += "################################################\n\n"
|
||||
return c
|
||||
}
|
||||
|
||||
type trigger struct {
|
||||
Event triggerRef `yaml:"event,omitempty"`
|
||||
Cron triggerRef `yaml:"cron,omitempty"`
|
||||
Target triggerRef `yaml:"target,omitempty"`
|
||||
Ref triggerRef `yaml:"ref,omitempty"`
|
||||
Repo triggerRef `yaml:"repo"`
|
||||
Branch triggerRef `yaml:"branch,omitempty"`
|
||||
}
|
||||
|
||||
type triggerRef struct {
|
||||
Include []string `yaml:"include,omitempty"`
|
||||
Exclude []string `yaml:"exclude,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML parses trigger references as either a list of strings, or as
|
||||
// include/exclude lists. Both are allowed by drone.
|
||||
func (v *triggerRef) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var rawItems []string
|
||||
if err := unmarshal(&rawItems); err == nil {
|
||||
v.Include = rawItems
|
||||
return nil
|
||||
}
|
||||
var regular struct {
|
||||
Include []string `yaml:"include,omitempty"`
|
||||
Exclude []string `yaml:"exclude,omitempty"`
|
||||
}
|
||||
if err := unmarshal(®ular); err == nil {
|
||||
v.Include = regular.Include
|
||||
v.Exclude = regular.Exclude
|
||||
return nil
|
||||
}
|
||||
return errors.New("can't unmarshal the value as either string or from_secret reference")
|
||||
}
|
||||
|
||||
type workspace struct {
|
||||
Path string `yaml:"path"`
|
||||
}
|
||||
|
||||
type clone struct {
|
||||
Disable bool `yaml:"disable"`
|
||||
}
|
||||
|
||||
type platform struct {
|
||||
OS string `yaml:"os"`
|
||||
Arch string `yaml:"arch"`
|
||||
}
|
||||
|
||||
type concurrency struct {
|
||||
Limit int `yaml:"limit"`
|
||||
}
|
||||
|
||||
type volume struct {
|
||||
Name string `yaml:"name"`
|
||||
Temp *volumeTemp `yaml:"temp,omitempty"`
|
||||
Claim *volumeClaim `yaml:"claim,omitempty"`
|
||||
}
|
||||
|
||||
type volumeTemp struct {
|
||||
Medium string `yaml:"medium,omitempty"`
|
||||
}
|
||||
|
||||
type volumeClaim struct {
|
||||
Name string `yaml:"name"`
|
||||
}
|
||||
|
||||
type service struct {
|
||||
Name string `yaml:"name"`
|
||||
Image string `yaml:"image"`
|
||||
Volumes []volumeRef `yaml:"volumes"`
|
||||
}
|
||||
|
||||
type volumeRef struct {
|
||||
Name string `yaml:"name"`
|
||||
Path string `yaml:"path"`
|
||||
}
|
||||
|
||||
type step struct {
|
||||
Name string `yaml:"name"`
|
||||
Image string `yaml:"image"`
|
||||
Commands []string `yaml:"commands"`
|
||||
Environment map[string]value `yaml:"environment,omitempty"`
|
||||
Volumes []volumeRef `yaml:"volumes,omitempty"`
|
||||
Settings map[string]value `yaml:"settings,omitempty"`
|
||||
Template []string `yaml:"template,omitempty"`
|
||||
When *condition `yaml:"when,omitempty"`
|
||||
Failure string `yaml:"failure,omitempty"`
|
||||
}
|
||||
|
||||
type condition struct {
|
||||
Status []string `yaml:"status,omitempty"`
|
||||
}
|
||||
|
||||
// value is a string value for key:value pairs like "environment" or
|
||||
// "settings". Values can be either inline strings (raw) or references to
|
||||
// secrets stored in Drone (fromSecret).
|
||||
type value struct {
|
||||
raw string
|
||||
fromSecret string
|
||||
}
|
||||
|
||||
type valueFromSecret struct {
|
||||
FromSecret string `yaml:"from_secret"`
|
||||
}
|
||||
|
||||
func (v value) MarshalYAML() (interface{}, error) {
|
||||
if v.raw != "" && v.fromSecret != "" {
|
||||
return nil, fmt.Errorf("value %+v has both raw and fromSecret set, can only have one", v)
|
||||
}
|
||||
if v.raw != "" {
|
||||
return v.raw, nil
|
||||
}
|
||||
if v.fromSecret != "" {
|
||||
return valueFromSecret{FromSecret: v.fromSecret}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("value has neither raw nor fromSecret set, need one")
|
||||
}
|
||||
|
||||
func (v *value) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
if err := unmarshal(&v.raw); err == nil {
|
||||
return nil
|
||||
}
|
||||
var fs valueFromSecret
|
||||
if err := unmarshal(&fs); err == nil {
|
||||
v.fromSecret = fs.FromSecret
|
||||
return nil
|
||||
}
|
||||
return errors.New("can't unmarshal the value as either string or from_secret reference")
|
||||
}
|
2
e
2
e
|
@ -1 +1 @@
|
|||
Subproject commit 3ec91b6ad87acdab25d262097edd12302c7c8b53
|
||||
Subproject commit 6fe0a12b02ca332c4c76724f0c15e47c6e41af1f
|
Loading…
Reference in a new issue