From fb99121a0d360f22a8d37dc74c060596a92b7113 Mon Sep 17 00:00:00 2001
From: fheinecke <23390735+fheinecke@users.noreply.github.com>
Date: Tue, 23 Aug 2022 19:42:27 -0500
Subject: [PATCH] Backport "Fix OS package repo promotion parallelism issue
 #15531" (#15696)

* Fix OS package repo promotion parallelism issue

* Added v8.3.17 migration

* Removed migrations
---
 .drone.yml             | 40 ++++++++++++++++-----------
 build.assets/README.md | 29 ++++++++++++++++++++
 dronegen/common.go     | 61 +++++++++++++++++++++++++++++++++++++++---
 dronegen/os_repos.go   | 36 ++++++++-----------------
 4 files changed, 123 insertions(+), 43 deletions(-)

diff --git a/.drone.yml b/.drone.yml
index 25e0450c24911..9bf70b358f2c3 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -5285,7 +5285,7 @@ volumes:
 ################################################
 # Generated using dronegen, do not edit by hand!
 # Use 'make dronegen' to update.
-# Generated at dronegen/os_repos.go:270
+# Generated at dronegen/os_repos.go:256
 ################################################
 
 kind: pipeline
@@ -5313,7 +5313,7 @@ steps:
 ################################################
 # Generated using dronegen, do not edit by hand!
 # Use 'make dronegen' to update.
-# Generated at dronegen/os_repos.go:294
+# Generated at dronegen/os_repos.go:280
 ################################################
 
 kind: pipeline
@@ -5342,16 +5342,21 @@ steps:
 - name: Check out code
   image: alpine/git:latest
   commands:
-  - mkdir -p "/go/src/github.com/gravitational/teleport"
+  - mkdir -pv "/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}"
+  - git init && git remote add origin ${DRONE_REMOTE_URL}
+  - git fetch origin
+  - git checkout -qf "${DRONE_TAG}"
+  depends_on:
+  - Verify build is tagged
 - name: Check if tag is prerelease
-  image: golang:1.17-alpine
+  image: golang:1.18-alpine
   commands:
   - cd "/go/src/github.com/gravitational/teleport/build.assets/tooling"
   - go run ./cmd/check -tag ${DRONE_TAG} -check prerelease || (echo '---> This is
-    a prerelease, not publishing ${DRONE_TAG} packages to APT repos' && exit 78)
+    a prerelease, not continuing promotion for ${DRONE_TAG}' && exit 78)
+  depends_on:
+  - Check out code
 - name: Download artifacts for "${DRONE_TAG}"
   image: amazon/aws-cli
   commands:
@@ -5422,7 +5427,7 @@ volumes:
 ################################################
 # Generated using dronegen, do not edit by hand!
 # Use 'make dronegen' to update.
-# Generated at dronegen/os_repos.go:270
+# Generated at dronegen/os_repos.go:256
 ################################################
 
 kind: pipeline
@@ -5450,7 +5455,7 @@ steps:
 ################################################
 # Generated using dronegen, do not edit by hand!
 # Use 'make dronegen' to update.
-# Generated at dronegen/os_repos.go:294
+# Generated at dronegen/os_repos.go:280
 ################################################
 
 kind: pipeline
@@ -5479,16 +5484,21 @@ steps:
 - name: Check out code
   image: alpine/git:latest
   commands:
-  - mkdir -p "/go/src/github.com/gravitational/teleport"
+  - mkdir -pv "/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}"
+  - git init && git remote add origin ${DRONE_REMOTE_URL}
+  - git fetch origin
+  - git checkout -qf "${DRONE_TAG}"
+  depends_on:
+  - Verify build is tagged
 - name: Check if tag is prerelease
-  image: golang:1.17-alpine
+  image: golang:1.18-alpine
   commands:
   - cd "/go/src/github.com/gravitational/teleport/build.assets/tooling"
   - go run ./cmd/check -tag ${DRONE_TAG} -check prerelease || (echo '---> This is
-    a prerelease, not publishing ${DRONE_TAG} packages to APT repos' && exit 78)
+    a prerelease, not continuing promotion for ${DRONE_TAG}' && exit 78)
+  depends_on:
+  - Check out code
 - name: Download artifacts for "${DRONE_TAG}"
   image: amazon/aws-cli
   commands:
@@ -6161,6 +6171,6 @@ volumes:
       medium: memory
 ---
 kind: signature
-hmac: 2c7f23660ddb216e42bcbe15185747f5764b627bd3f0b56529d25254a8511b23
+hmac: 708f816d2aadf332cef34fc19f2ffe93a4cddf78382e078cd51ce1f6114b84c8
 
 ...
diff --git a/build.assets/README.md b/build.assets/README.md
index cc7b9a372d34b..f79703dde7028 100644
--- a/build.assets/README.md
+++ b/build.assets/README.md
@@ -26,3 +26,32 @@ Or simply copy the binary out of the image using a volume (it will be copied to
 ```
 docker run -v $(pwd)/build:/builds -it teleportbuilder cp /gopath/src/github.com/gravitational/teleport/teleport.tgz /builds
 ```
+
+# OS package repo migrations
+
+An OS package repo migration is semi-manually publishing specific releases to the new APT and YUM repos. This is required in several situations:
+* A customer requests that we add an older version to the repos
+* We add another OS package repo (for example APK)
+* A OS package promotion fails (for example https://drone.platform.teleport.sh/gravitational/teleport/14666/1/3), requires a PR to fix, and we don't want to cut another minor version
+
+Multiple migrations can be performed at once. To run a migration do the following:
+1. Clone https://github.com/gravitational/teleport.git.
+2. Change to the directory the repo was cloned to.
+3. Create a new branch from master.
+4. Add the Teleport versions you wish to migration as demonstrated here: https://github.com/gravitational/teleport/commit/151a2f489e3116fc7ce8f55e056529361d3233a6#diff-2e3a64c97d186491e06fb2c7ead081b7ace2b67c4a4d974a563daf7c117a2c50.
+5. Set the `migrationBranch` variable to the name of the branch you created in (3) as demonstrated here: https://github.com/gravitational/teleport/commit/151a2f489e3116fc7ce8f55e056529361d3233a6#diff-2e3a64c97d186491e06fb2c7ead081b7ace2b67c4a4d974a563daf7c117a2c50.
+6. Get your Drone credentials from here: https://drone.platform.teleport.sh/account.
+7. Export your drone credentials as shown under "Example CLI Usage" on the Drone account page
+8. Open a new terminal.
+9. Run `tsh app login drone` and follow any prompts.
+10. Run `tsh proxy app drone` and copy the printed socket. This should look something like `127.0.0.1:60982`
+11. Switch back to your previous terminal.
+12. Run `export DRONE_SERVER=http://{host:port}`, replacing `{host:port}` with the data you copied in (10)
+13. Run `make dronegen`
+14. Commit the two changed files and push/publish the branch
+15. Open a PR merging your changes into master via https://github.com/gravitational/teleport/compare
+16. Under the "checks" section, click "details" on the check labeled "continuous-integration/drone/push"
+17. Once the pipelines complete, comment out the versions you added and blank out the `migrationBranch` string set in (4, 5) as demonstrated here: https://github.com/gravitational/teleport/pull/15531/commits/9095880560cfe6c93e491e39a7604b1faf72c600#diff-2e3a64c97d186491e06fb2c7ead081b7ace2b67c4a4d974a563daf7c117a2c50
+18. Run `make dronegen`
+19. Commit and push the changes.
+20. Merge the PR and backport if required.
\ No newline at end of file
diff --git a/dronegen/common.go b/dronegen/common.go
index 70877731462b1..3edf86f7c0a93 100644
--- a/dronegen/common.go
+++ b/dronegen/common.go
@@ -19,6 +19,7 @@ import (
 	"fmt"
 	"log"
 	"os/exec"
+	"path"
 	"strings"
 )
 
@@ -90,6 +91,16 @@ func pushTriggerFor(branches ...string) trigger {
 	return t
 }
 
+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`,
+		fmt.Sprintf("git checkout -qf %q", commit),
+	}
+}
+
 type buildType struct {
 	os              string
 	arch            string
@@ -99,9 +110,10 @@ type buildType struct {
 }
 
 // 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
+//
+//	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
 
@@ -223,3 +235,46 @@ func waitForDockerStep() step {
 		Volumes: dockerVolumeRefs(),
 	}
 }
+
+func verifyValidPromoteRunSteps(checkoutPath, commit string, isParallelismEnabled bool) []step {
+	tagStep := verifyTaggedStep()
+	cloneStep := cloneRepoStep(checkoutPath, commit)
+	verifyStep := verifyNotPrereleaseStep(checkoutPath)
+
+	if isParallelismEnabled {
+		cloneStep.DependsOn = []string{tagStep.Name}
+		verifyStep.DependsOn = []string{cloneStep.Name}
+	}
+
+	return []step{tagStep, cloneStep, verifyStep}
+}
+
+func verifyTaggedStep() step {
+	return step{
+		Name:  "Verify build is tagged",
+		Image: "alpine:latest",
+		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",
+		Commands: cloneRepoCommands(clonePath, commit),
+	}
+}
+
+func verifyNotPrereleaseStep(checkoutPath string) step {
+	return step{
+		Name:  "Check if tag is prerelease",
+		Image: "golang:1.18-alpine",
+		Commands: []string{
+			fmt.Sprintf("cd %q", path.Join(checkoutPath, "build.assets", "tooling")),
+			"go run ./cmd/check -tag ${DRONE_TAG} -check prerelease || (echo '---> This is a prerelease, not continuing promotion for ${DRONE_TAG}' && exit 78)",
+		},
+	}
+}
diff --git a/dronegen/os_repos.go b/dronegen/os_repos.go
index 8df081ba37b59..2c2c71a5b6ccd 100644
--- a/dronegen/os_repos.go
+++ b/dronegen/os_repos.go
@@ -31,11 +31,11 @@ func promoteBuildOsRepoPipelines() []pipeline {
 
 // Used for one-off migrations of older versions.
 // Use cases include:
-//  * We want to support another OS while providing backwards compatibility
-//  * We want to support another OS version while providing backwards compatibility
-//  * A customer wants to be able to install an older version via APT/YUM even if we
-//      no longer support it
-//  * RPM migrations after new YUM pipeline is done
+//   - We want to support another OS while providing backwards compatibility
+//   - We want to support another OS version while providing backwards compatibility
+//   - A customer wants to be able to install an older version via APT/YUM even if we
+//     no longer support it
+//   - RPM migrations after new YUM pipeline is done
 func artifactMigrationPipeline() []pipeline {
 	migrationVersions := []string{
 		// These versions were migrated as a part of the new `promoteAptPipeline`
@@ -59,6 +59,7 @@ func artifactMigrationPipeline() []pipeline {
 		// "v8.3.14",
 		// "v8.3.15",
 		// "v8.3.16",
+		// "v8.3.17",
 		// "v9.0.0",
 		// "v9.0.1",
 		// "v9.0.2",
@@ -83,9 +84,13 @@ func artifactMigrationPipeline() []pipeline {
 		// "v9.3.10",
 		// "v9.3.12",
 		// "v9.3.13",
+		// "v9.3.14",
+		// "v9.3.18",
 		// "v10.0.0",
 		// "v10.0.1",
 		// "v10.0.2",
+		// "v10.1.2",
+		// "v10.1.4",
 	}
 	// Pushing to this branch will trigger the listed versions to be migrated. Typically this should be
 	// the branch that these changes are being committed to.
@@ -192,26 +197,7 @@ func (optpb *OsPackageToolPipelineBuilder) buildPromoteOsPackagePipeline() pipel
 	p.Trigger = triggerPromote
 	p.Trigger.Repo.Include = []string{"gravitational/teleport"}
 
-	setupSteps := []step{
-		{
-			Name:  "Verify build is tagged",
-			Image: "alpine:latest",
-			Commands: []string{
-				"[ -n ${DRONE_TAG} ] || (echo 'DRONE_TAG is not set. Is the commit tagged?' && exit 1)",
-			},
-		},
-	}
-	setupSteps = append(setupSteps, p.Steps...)
-	setupSteps = append(setupSteps,
-		step{
-			Name:  "Check if tag is prerelease",
-			Image: "golang:1.17-alpine",
-			Commands: []string{
-				fmt.Sprintf("cd %q", path.Join(checkoutPath, "build.assets", "tooling")),
-				"go run ./cmd/check -tag ${DRONE_TAG} -check prerelease || (echo '---> This is a prerelease, not publishing ${DRONE_TAG} packages to APT repos' && exit 78)",
-			},
-		},
-	)
+	setupSteps := verifyValidPromoteRunSteps(checkoutPath, commitName, true)
 
 	setupStepNames := make([]string, 0, len(setupSteps))
 	for _, setupStep := range setupSteps {