831 lines
32 KiB
JavaScript
831 lines
32 KiB
JavaScript
import {
|
|
argoApplicationName,
|
|
buildkitRunnerImage,
|
|
ciToolsRunnerImage,
|
|
defaultDevBaseImage,
|
|
defaultRegistryPrefix,
|
|
defaultServiceIds,
|
|
defaultSourceRepo,
|
|
gitopsPathFor,
|
|
gitopsPathForProfile,
|
|
gitopsTargetForProfile,
|
|
isRuntimeLane,
|
|
namespaceNameForProfile,
|
|
primitiveValidationTasks,
|
|
proxyEnv,
|
|
serviceIdsForLane,
|
|
servicesParamForLane
|
|
} from "./core.mjs";
|
|
import {
|
|
ciTimingShellFunction,
|
|
collectArtifactsScript,
|
|
controlPlaneReconcileScript,
|
|
gitopsPromoteScript,
|
|
perServicePublishScript,
|
|
planArtifactsScript,
|
|
pollerScript,
|
|
prepareSourceScript,
|
|
primitiveValidationTask,
|
|
primitiveValidationTaskNames
|
|
} from "./tekton-scripts.mjs";
|
|
|
|
function ciLaneSettings(argsOrLane = "node") {
|
|
const lane = typeof argsOrLane === "string" ? argsOrLane : argsOrLane.lane;
|
|
if (isRuntimeLane(lane)) {
|
|
const namespace = namespaceNameForProfile(lane);
|
|
return {
|
|
lane,
|
|
profile: lane,
|
|
gitopsTarget: lane,
|
|
pipelineName: `${namespace}-ci-image-publish`,
|
|
pipelineRunPrefix: `${namespace}-ci-poll`,
|
|
serviceAccountName: `${namespace}-tekton-runner`,
|
|
pollerName: `${namespace}-branch-poller`,
|
|
controlPlaneReconcilerName: `${namespace}-control-plane-reconciler`,
|
|
tektonDir: `tekton-${lane}`,
|
|
catalogPath: typeof argsOrLane === "object" ? argsOrLane.catalogPath : `deploy/artifact-catalog.${lane}.json`,
|
|
imageTagMode: typeof argsOrLane === "object" ? argsOrLane.imageTagMode : "full",
|
|
runtimeNamespace: namespace
|
|
};
|
|
}
|
|
return {
|
|
lane: "node",
|
|
profile: "dev",
|
|
gitopsTarget: "node",
|
|
pipelineName: "hwlab-node-ci-image-publish",
|
|
pipelineRunPrefix: "hwlab-node-ci-poll",
|
|
serviceAccountName: "hwlab-tekton-runner",
|
|
pollerName: "hwlab-node-branch-poller",
|
|
controlPlaneReconcilerName: "hwlab-node-control-plane-reconciler",
|
|
tektonDir: "tekton",
|
|
catalogPath: "deploy/artifact-catalog.dev.json",
|
|
imageTagMode: "short",
|
|
runtimeNamespace: "hwlab-dev"
|
|
};
|
|
}
|
|
|
|
function tektonRbac(args = { lane: "node" }) {
|
|
const settings = ciLaneSettings(args);
|
|
if (isRuntimeLane(settings.lane)) {
|
|
const runtimeObserverName = `${settings.runtimeNamespace}-runtime-observer`;
|
|
const pipelineRunWriterName = `${settings.runtimeNamespace}-pipelinerun-writer`;
|
|
const argoReconcilerName = `${settings.runtimeNamespace}-argocd-reconciler`;
|
|
return {
|
|
apiVersion: "v1",
|
|
kind: "List",
|
|
items: [
|
|
{ apiVersion: "v1", kind: "Namespace", metadata: { name: "hwlab-ci" } },
|
|
{ apiVersion: "v1", kind: "ServiceAccount", metadata: { name: settings.serviceAccountName, namespace: "hwlab-ci" } },
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "Role",
|
|
metadata: { name: runtimeObserverName, namespace: settings.runtimeNamespace },
|
|
rules: [
|
|
{ apiGroups: [""], resources: ["pods", "services", "configmaps"], verbs: ["get", "list", "watch"] },
|
|
{ apiGroups: ["apps"], resources: ["deployments", "statefulsets"], verbs: ["get", "list", "watch"] },
|
|
{ apiGroups: ["batch"], resources: ["jobs"], verbs: ["get", "list", "watch"] }
|
|
]
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "RoleBinding",
|
|
metadata: { name: runtimeObserverName, namespace: settings.runtimeNamespace },
|
|
subjects: [{ kind: "ServiceAccount", name: settings.serviceAccountName, namespace: "hwlab-ci" }],
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: runtimeObserverName }
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "Role",
|
|
metadata: { name: pipelineRunWriterName, namespace: "hwlab-ci" },
|
|
rules: [
|
|
{ apiGroups: ["tekton.dev"], resources: ["pipelineruns"], verbs: ["get", "list", "create"] },
|
|
{ apiGroups: ["tekton.dev"], resources: ["pipelines"], verbs: ["get", "patch", "create"] },
|
|
{ apiGroups: ["batch"], resources: ["cronjobs"], verbs: ["get", "patch", "create"] },
|
|
{ apiGroups: ["rbac.authorization.k8s.io"], resources: ["roles", "rolebindings"], verbs: ["get", "patch", "create"] },
|
|
{ apiGroups: [""], resources: ["serviceaccounts"], verbs: ["get", "patch", "create"] }
|
|
]
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "RoleBinding",
|
|
metadata: { name: pipelineRunWriterName, namespace: "hwlab-ci" },
|
|
subjects: [{ kind: "ServiceAccount", name: settings.serviceAccountName, namespace: "hwlab-ci" }],
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: pipelineRunWriterName }
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "Role",
|
|
metadata: { name: argoReconcilerName, namespace: "argocd" },
|
|
rules: [
|
|
{ apiGroups: ["argoproj.io"], resources: ["applications", "appprojects"], verbs: ["get", "patch", "create"] },
|
|
{ apiGroups: ["rbac.authorization.k8s.io"], resources: ["roles", "rolebindings"], verbs: ["get", "patch", "create"] }
|
|
]
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "RoleBinding",
|
|
metadata: { name: argoReconcilerName, namespace: "argocd" },
|
|
subjects: [{ kind: "ServiceAccount", name: settings.serviceAccountName, namespace: "hwlab-ci" }],
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: argoReconcilerName }
|
|
}
|
|
]
|
|
};
|
|
}
|
|
return {
|
|
apiVersion: "v1",
|
|
kind: "List",
|
|
items: [
|
|
{ apiVersion: "v1", kind: "Namespace", metadata: { name: "hwlab-ci" } },
|
|
{ apiVersion: "v1", kind: "ServiceAccount", metadata: { name: "hwlab-tekton-runner", namespace: "hwlab-ci" } },
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "Role",
|
|
metadata: { name: "hwlab-node-control-plane-reconciler", namespace: "hwlab-dev" },
|
|
rules: [
|
|
{ apiGroups: ["rbac.authorization.k8s.io"], resources: ["roles", "rolebindings"], verbs: ["get", "patch", "create"] }
|
|
]
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "RoleBinding",
|
|
metadata: { name: "hwlab-node-control-plane-reconciler", namespace: "hwlab-dev" },
|
|
subjects: [{ kind: "ServiceAccount", name: "hwlab-tekton-runner", namespace: "hwlab-ci" }],
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: "hwlab-node-control-plane-reconciler" }
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "Role",
|
|
metadata: { name: "hwlab-tekton-runtime-observer", namespace: "hwlab-dev" },
|
|
rules: [
|
|
{ apiGroups: [""], resources: ["pods", "services", "configmaps"], verbs: ["get", "list", "watch"] },
|
|
{ apiGroups: ["apps"], resources: ["deployments", "statefulsets"], verbs: ["get", "list", "watch"] },
|
|
{ apiGroups: ["batch"], resources: ["jobs"], verbs: ["get", "list", "watch"] }
|
|
]
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "RoleBinding",
|
|
metadata: { name: "hwlab-tekton-runtime-observer", namespace: "hwlab-dev" },
|
|
subjects: [{ kind: "ServiceAccount", name: "hwlab-tekton-runner", namespace: "hwlab-ci" }],
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: "hwlab-tekton-runtime-observer" }
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "Role",
|
|
metadata: { name: "hwlab-tekton-pipelinerun-writer", namespace: "hwlab-ci" },
|
|
rules: [
|
|
{ apiGroups: ["tekton.dev"], resources: ["pipelineruns"], verbs: ["get", "list", "create"] },
|
|
{ apiGroups: ["tekton.dev"], resources: ["pipelines"], verbs: ["get", "patch", "create"] },
|
|
{ apiGroups: ["batch"], resources: ["cronjobs"], verbs: ["get", "patch", "create"] },
|
|
{ apiGroups: ["rbac.authorization.k8s.io"], resources: ["roles", "rolebindings"], verbs: ["get", "patch", "create"] },
|
|
{ apiGroups: [""], resources: ["serviceaccounts"], verbs: ["get", "patch", "create"] }
|
|
]
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "RoleBinding",
|
|
metadata: { name: "hwlab-tekton-pipelinerun-writer", namespace: "hwlab-ci" },
|
|
subjects: [{ kind: "ServiceAccount", name: "hwlab-tekton-runner", namespace: "hwlab-ci" }],
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: "hwlab-tekton-pipelinerun-writer" }
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
function buildTaskName(serviceId) {
|
|
return `build-${serviceId}`;
|
|
}
|
|
|
|
function affectedResultName(serviceId) {
|
|
return `affected-${serviceId}`;
|
|
}
|
|
|
|
function buildResultName(serviceId) {
|
|
return `build-${serviceId}`;
|
|
}
|
|
|
|
const serviceResultFields = Object.freeze([
|
|
"status",
|
|
"service-id",
|
|
"image",
|
|
"image-tag",
|
|
"digest",
|
|
"repository-digest",
|
|
"source-commit-id",
|
|
"component-input-hash",
|
|
"environment-input-hash",
|
|
"code-input-hash",
|
|
"runtime-mode",
|
|
"boot-repo",
|
|
"boot-commit",
|
|
"boot-sh",
|
|
"build-created-at",
|
|
"build-backend",
|
|
"reused-from",
|
|
"go-base-image",
|
|
"go-base-image-status",
|
|
"go-base-digest",
|
|
"go-base-build-duration-ms",
|
|
"go-base-buildkit-cache-ref"
|
|
]);
|
|
|
|
function serviceResultParamName(serviceId, field) {
|
|
return `${serviceId}-${field}`;
|
|
}
|
|
|
|
function serviceResultEnvName(serviceId, field) {
|
|
return `HWLAB_SERVICE_RESULT_${serviceId.toUpperCase().replace(/[^A-Z0-9]+/gu, "_")}_${field.toUpperCase().replace(/[^A-Z0-9]+/gu, "_")}`;
|
|
}
|
|
|
|
function serviceResultParams(serviceIds = defaultServiceIds) {
|
|
return serviceIds.flatMap((serviceId) => serviceResultFields.map((field) => ({ name: serviceResultParamName(serviceId, field), default: "" })));
|
|
}
|
|
|
|
function serviceResultParamValues(serviceIds = defaultServiceIds) {
|
|
return serviceIds.flatMap((serviceId) => serviceResultFields.map((field) => ({
|
|
name: serviceResultParamName(serviceId, field),
|
|
value: `$(tasks.${buildTaskName(serviceId)}.results.${field})`
|
|
})));
|
|
}
|
|
|
|
function serviceResultEnv(serviceIds = defaultServiceIds) {
|
|
return serviceIds.flatMap((serviceId) => serviceResultFields.map((field) => ({
|
|
name: serviceResultEnvName(serviceId, field),
|
|
value: `$(params.${serviceResultParamName(serviceId, field)})`
|
|
})));
|
|
}
|
|
|
|
function serviceWorkVolume() {
|
|
return { name: "service-work", emptyDir: {} };
|
|
}
|
|
|
|
function buildkitBinVolume() {
|
|
return { name: "buildkit-bin", emptyDir: {} };
|
|
}
|
|
|
|
function buildkitRunVolume() {
|
|
return { name: "buildkit-run", emptyDir: {} };
|
|
}
|
|
|
|
function prepareBuildkitClientStep() {
|
|
return {
|
|
name: "prepare-buildkit-client",
|
|
image: buildkitRunnerImage,
|
|
securityContext: { runAsUser: 0, runAsGroup: 0 },
|
|
volumeMounts: [{ name: "buildkit-bin", mountPath: "/workspace/buildkit-bin" }],
|
|
script: `#!/bin/sh
|
|
set -eu
|
|
mkdir -p /workspace/buildkit-bin
|
|
cp /usr/bin/buildctl /workspace/buildkit-bin/
|
|
chmod +x /workspace/buildkit-bin/*
|
|
`
|
|
};
|
|
}
|
|
|
|
function buildkitSidecar() {
|
|
return {
|
|
name: "buildkitd",
|
|
image: buildkitRunnerImage,
|
|
args: ["--addr", "unix:///workspace/buildkit-run/buildkitd.sock", "--oci-worker-no-process-sandbox"],
|
|
env: proxyEnv(),
|
|
volumeMounts: [{ name: "buildkit-run", mountPath: "/workspace/buildkit-run" }],
|
|
securityContext: {
|
|
runAsUser: 1000,
|
|
runAsGroup: 1000,
|
|
allowPrivilegeEscalation: true,
|
|
appArmorProfile: { type: "Unconfined" },
|
|
seccompProfile: { type: "Unconfined" }
|
|
}
|
|
};
|
|
}
|
|
|
|
function planArtifactsTask({ serviceIds = defaultServiceIds } = {}) {
|
|
return {
|
|
name: "plan-artifacts",
|
|
runAfter: primitiveValidationTaskNames(),
|
|
workspaces: [{ name: "source", workspace: "source" }],
|
|
taskSpec: {
|
|
params: [
|
|
{ name: "git-url" },
|
|
{ name: "git-read-url" },
|
|
{ name: "gitops-branch" },
|
|
{ name: "lane" },
|
|
{ name: "revision" },
|
|
{ name: "catalog-path" },
|
|
{ name: "registry-prefix" },
|
|
{ name: "services" }
|
|
],
|
|
results: serviceIds.flatMap((serviceId) => [
|
|
{ name: affectedResultName(serviceId), description: `${serviceId} rollout affected according to ci-plan` },
|
|
{ name: buildResultName(serviceId), description: `${serviceId} image build required according to ci-plan` }
|
|
]),
|
|
workspaces: [{ name: "source" }],
|
|
steps: [{
|
|
name: "plan",
|
|
image: ciToolsRunnerImage,
|
|
env: [
|
|
{ name: "HWLAB_SELECTED_SERVICES", value: "$(params.services)" },
|
|
{ name: "HWLAB_DEV_REGISTRY_K8S_NATIVE", value: "1" },
|
|
{ name: "HWLAB_TEKTON_PIPELINERUN", value: "$(context.pipelineRun.name)" },
|
|
...proxyEnv()
|
|
],
|
|
script: planArtifactsScript()
|
|
}]
|
|
},
|
|
params: [
|
|
{ name: "git-url", value: "$(params.git-url)" },
|
|
{ name: "git-read-url", value: "$(params.git-read-url)" },
|
|
{ name: "gitops-branch", value: "$(params.gitops-branch)" },
|
|
{ name: "lane", value: "$(params.lane)" },
|
|
{ name: "revision", value: "$(params.revision)" },
|
|
{ name: "catalog-path", value: "$(params.catalog-path)" },
|
|
{ name: "registry-prefix", value: "$(params.registry-prefix)" },
|
|
{ name: "services", value: "$(params.services)" }
|
|
]
|
|
};
|
|
}
|
|
|
|
function perServiceBuildTask(serviceId, { runAfter = ["plan-artifacts"] } = {}) {
|
|
return {
|
|
name: buildTaskName(serviceId),
|
|
runAfter,
|
|
workspaces: [{ name: "source", workspace: "source" }],
|
|
when: [{ input: `$(tasks.plan-artifacts.results.${buildResultName(serviceId)})`, operator: "in", values: ["true"] }],
|
|
taskSpec: {
|
|
params: [
|
|
{ name: "revision" },
|
|
{ name: "lane" },
|
|
{ name: "catalog-path" },
|
|
{ name: "image-tag-mode" },
|
|
{ name: "registry-prefix" },
|
|
{ name: "service-id" },
|
|
{ name: "base-image" },
|
|
{ name: "build-cache-mode" }
|
|
],
|
|
results: serviceResultFields.map((field) => ({ name: field, description: `${serviceId} ${field}` })),
|
|
workspaces: [{ name: "source" }],
|
|
volumes: [serviceWorkVolume(), buildkitBinVolume(), buildkitRunVolume()],
|
|
sidecars: [buildkitSidecar()],
|
|
steps: [
|
|
prepareBuildkitClientStep(),
|
|
{
|
|
name: "publish",
|
|
image: ciToolsRunnerImage,
|
|
securityContext: { runAsUser: 1000, runAsGroup: 1000 },
|
|
env: proxyEnv(),
|
|
volumeMounts: [{ name: "service-work", mountPath: "/workspace/service-work" }, { name: "buildkit-bin", mountPath: "/workspace/buildkit-bin" }, { name: "buildkit-run", mountPath: "/workspace/buildkit-run" }],
|
|
script: perServicePublishScript()
|
|
}
|
|
]
|
|
},
|
|
params: [
|
|
{ name: "revision", value: "$(params.revision)" },
|
|
{ name: "lane", value: "$(params.lane)" },
|
|
{ name: "catalog-path", value: "$(params.catalog-path)" },
|
|
{ name: "image-tag-mode", value: "$(params.image-tag-mode)" },
|
|
{ name: "registry-prefix", value: "$(params.registry-prefix)" },
|
|
{ name: "service-id", value: serviceId },
|
|
{ name: "base-image", value: "$(params.base-image)" },
|
|
{ name: "build-cache-mode", value: "$(params.build-cache-mode)" }
|
|
]
|
|
};
|
|
}
|
|
|
|
function collectArtifactsTask({ serviceIds = defaultServiceIds } = {}) {
|
|
return {
|
|
name: "collect-artifacts",
|
|
runAfter: serviceIds.map(buildTaskName),
|
|
workspaces: [{ name: "source", workspace: "source" }],
|
|
taskSpec: {
|
|
params: [
|
|
{ name: "revision" },
|
|
{ name: "lane" },
|
|
{ name: "catalog-path" },
|
|
{ name: "image-tag-mode" },
|
|
{ name: "registry-prefix" },
|
|
{ name: "services" },
|
|
{ name: "base-image" }
|
|
],
|
|
workspaces: [{ name: "source" }],
|
|
steps: [{ name: "collect", image: ciToolsRunnerImage, env: proxyEnv(), script: collectArtifactsScript() }]
|
|
},
|
|
params: [
|
|
{ name: "revision", value: "$(params.revision)" },
|
|
{ name: "lane", value: "$(params.lane)" },
|
|
{ name: "catalog-path", value: "$(params.catalog-path)" },
|
|
{ name: "image-tag-mode", value: "$(params.image-tag-mode)" },
|
|
{ name: "registry-prefix", value: "$(params.registry-prefix)" },
|
|
{ name: "services", value: "$(params.services)" },
|
|
{ name: "base-image", value: "$(params.base-image)" }
|
|
]
|
|
};
|
|
}
|
|
|
|
function runtimeReadyScript() {
|
|
return `#!/bin/sh
|
|
set -eu
|
|
${ciTimingShellFunction()}
|
|
export HWLAB_TEKTON_PIPELINERUN="$(context.pipelineRun.name)"
|
|
export HWLAB_TEKTON_TASKRUN="$(context.taskRun.name)"
|
|
export HWLAB_TEKTON_TASK="runtime-ready"
|
|
export HWLAB_SOURCE_REVISION="$(params.revision)"
|
|
export HWLAB_RUNTIME_READY_TIMEOUT_MS="$(params.timeout-ms)"
|
|
cd /workspace/source/repo
|
|
node scripts/ci/runtime-ready.mjs
|
|
`;
|
|
}
|
|
|
|
function runtimeReadyTask({ profile = "dev" } = {}) {
|
|
return {
|
|
name: "runtime-ready",
|
|
runAfter: ["gitops-promote"],
|
|
workspaces: [{ name: "source", workspace: "source" }],
|
|
taskSpec: {
|
|
params: [{ name: "revision" }, { name: "runtime-namespace" }, { name: "gitops-target" }, { name: "argo-application" }, { name: "timeout-ms" }],
|
|
workspaces: [{ name: "source" }],
|
|
steps: [{
|
|
name: "runtime-ready",
|
|
image: ciToolsRunnerImage,
|
|
env: [
|
|
...proxyEnv(),
|
|
{ name: "HWLAB_RUNTIME_NAMESPACE", value: "$(params.runtime-namespace)" },
|
|
{ name: "HWLAB_GITOPS_TARGET", value: "$(params.gitops-target)" },
|
|
{ name: "HWLAB_ARGO_APPLICATION", value: "$(params.argo-application)" }
|
|
],
|
|
script: runtimeReadyScript()
|
|
}]
|
|
},
|
|
params: [
|
|
{ name: "revision", value: "$(params.revision)" },
|
|
{ name: "runtime-namespace", value: namespaceNameForProfile(profile) },
|
|
{ name: "gitops-target", value: gitopsTargetForProfile(profile) },
|
|
{ name: "argo-application", value: argoApplicationName(profile) },
|
|
{ name: "timeout-ms", value: "$(params.runtime-ready-timeout-ms)" }
|
|
]
|
|
};
|
|
}
|
|
|
|
function imagePublishTaskSet(args = { lane: "node" }, deploy = null) {
|
|
const serviceIds = serviceIdsForLane(args.lane, deploy);
|
|
const buildTasks = serviceIds.map((serviceId) => perServiceBuildTask(serviceId, {
|
|
runAfter: ["plan-artifacts"]
|
|
}));
|
|
return [
|
|
planArtifactsTask({ serviceIds }),
|
|
...buildTasks,
|
|
collectArtifactsTask({ serviceIds })
|
|
];
|
|
}
|
|
|
|
function tektonPipeline(args = { lane: "node" }, deploy = null) {
|
|
const settings = ciLaneSettings(args);
|
|
const runtimePath = gitopsPathForProfile(args, settings.profile);
|
|
const preFlushRuntimeReadyTasks = isRuntimeLane(settings.lane)
|
|
? []
|
|
: [{
|
|
...runtimeReadyTask({ profile: settings.profile }),
|
|
when: [{ input: "$(tasks.gitops-promote.results.runtime-ready-required)", operator: "in", values: ["true"] }]
|
|
}];
|
|
return {
|
|
apiVersion: "tekton.dev/v1",
|
|
kind: "Pipeline",
|
|
metadata: {
|
|
name: settings.pipelineName,
|
|
namespace: "hwlab-ci",
|
|
labels: {
|
|
"app.kubernetes.io/part-of": "hwlab",
|
|
"hwlab.pikastech.local/gitops-target": settings.gitopsTarget
|
|
},
|
|
annotations: {
|
|
"hwlab.pikastech.local/source-config": "scripts/gitops-render.mjs#tekton-native-primitive-ci",
|
|
"hwlab.pikastech.local/ci-contract": "tekton-native-primitive-tasks",
|
|
"hwlab.pikastech.local/policy": "native-per-service-taskrun-image-publish"
|
|
}
|
|
},
|
|
spec: {
|
|
params: [
|
|
{ name: "git-url", type: "string", default: defaultSourceRepo },
|
|
{ name: "git-read-url", type: "string", default: args.gitReadUrl },
|
|
{ name: "git-write-url", type: "string", default: args.gitWriteUrl },
|
|
{ name: "source-branch", type: "string", default: args.sourceBranch },
|
|
{ name: "gitops-branch", type: "string", default: args.gitopsBranch },
|
|
{ name: "lane", type: "string", default: settings.lane },
|
|
{ name: "catalog-path", type: "string", default: args.catalogPath || settings.catalogPath },
|
|
{ name: "image-tag-mode", type: "string", default: args.imageTagMode || settings.imageTagMode },
|
|
{ name: "runtime-path", type: "string", default: runtimePath },
|
|
{ name: "revision", type: "string", description: "Full git commit SHA; the only source truth for image tags." },
|
|
{ name: "registry-prefix", type: "string", default: defaultRegistryPrefix },
|
|
{ name: "services", type: "string", default: servicesParamForLane(args.lane, deploy) },
|
|
{ name: "base-image", type: "string", default: defaultDevBaseImage },
|
|
{ name: "build-cache-mode", type: "string", default: "registry" },
|
|
{ name: "runtime-ready-timeout-ms", type: "string", default: "60000" }
|
|
],
|
|
workspaces: [
|
|
{ name: "source" },
|
|
{ name: "git-ssh" }
|
|
],
|
|
tasks: [
|
|
{
|
|
name: "prepare-source",
|
|
workspaces: [
|
|
{ name: "source", workspace: "source" },
|
|
{ name: "git-ssh", workspace: "git-ssh" }
|
|
],
|
|
taskSpec: {
|
|
params: [
|
|
{ name: "git-url" },
|
|
{ name: "git-read-url" },
|
|
{ name: "source-branch" },
|
|
{ name: "gitops-branch" },
|
|
{ name: "lane" },
|
|
{ name: "catalog-path" },
|
|
{ name: "image-tag-mode" },
|
|
{ name: "registry-prefix" },
|
|
{ name: "services" },
|
|
{ name: "revision" }
|
|
],
|
|
workspaces: [{ name: "source" }, { name: "git-ssh" }],
|
|
steps: [{ name: "prepare-source", image: ciToolsRunnerImage, env: proxyEnv(), script: prepareSourceScript() }]
|
|
},
|
|
params: [
|
|
{ name: "git-url", value: "$(params.git-url)" },
|
|
{ name: "git-read-url", value: "$(params.git-read-url)" },
|
|
{ name: "source-branch", value: "$(params.source-branch)" },
|
|
{ name: "gitops-branch", value: "$(params.gitops-branch)" },
|
|
{ name: "lane", value: "$(params.lane)" },
|
|
{ name: "catalog-path", value: "$(params.catalog-path)" },
|
|
{ name: "image-tag-mode", value: "$(params.image-tag-mode)" },
|
|
{ name: "registry-prefix", value: "$(params.registry-prefix)" },
|
|
{ name: "services", value: "$(params.services)" },
|
|
{ name: "revision", value: "$(params.revision)" }
|
|
]
|
|
},
|
|
...primitiveValidationTasks.map(primitiveValidationTask),
|
|
...imagePublishTaskSet(args, deploy),
|
|
{
|
|
name: "gitops-promote",
|
|
runAfter: ["collect-artifacts"],
|
|
workspaces: [
|
|
{ name: "source", workspace: "source" },
|
|
{ name: "git-ssh", workspace: "git-ssh" }
|
|
],
|
|
taskSpec: {
|
|
params: [
|
|
{ name: "git-url" },
|
|
{ name: "git-read-url" },
|
|
{ name: "git-write-url" },
|
|
{ name: "source-branch" },
|
|
{ name: "gitops-branch" },
|
|
{ name: "lane" },
|
|
{ name: "catalog-path" },
|
|
{ name: "image-tag-mode" },
|
|
{ name: "runtime-path" },
|
|
{ name: "revision" },
|
|
{ name: "registry-prefix" }
|
|
],
|
|
results: [{ name: "runtime-ready-required", description: "true when GitOps promotion changed runtime desired state and runtime readiness must be observed" }],
|
|
workspaces: [{ name: "source" }, { name: "git-ssh" }],
|
|
steps: [{ name: "promote", image: ciToolsRunnerImage, env: proxyEnv(), script: gitopsPromoteScript() }]
|
|
},
|
|
params: [
|
|
{ name: "git-url", value: "$(params.git-url)" },
|
|
{ name: "git-read-url", value: "$(params.git-read-url)" },
|
|
{ name: "git-write-url", value: "$(params.git-write-url)" },
|
|
{ name: "source-branch", value: "$(params.source-branch)" },
|
|
{ name: "gitops-branch", value: "$(params.gitops-branch)" },
|
|
{ name: "lane", value: "$(params.lane)" },
|
|
{ name: "catalog-path", value: "$(params.catalog-path)" },
|
|
{ name: "image-tag-mode", value: "$(params.image-tag-mode)" },
|
|
{ name: "runtime-path", value: "$(params.runtime-path)" },
|
|
{ name: "revision", value: "$(params.revision)" },
|
|
{ name: "registry-prefix", value: "$(params.registry-prefix)" }
|
|
]
|
|
},
|
|
...preFlushRuntimeReadyTasks
|
|
]
|
|
}
|
|
};
|
|
}
|
|
|
|
function tektonPipelineRunTemplate({ source, args }) {
|
|
const settings = ciLaneSettings(args);
|
|
const runtimePath = gitopsPathForProfile(args, settings.profile);
|
|
return {
|
|
apiVersion: "tekton.dev/v1",
|
|
kind: "PipelineRun",
|
|
metadata: {
|
|
generateName: `${settings.pipelineRunPrefix}-manual-`,
|
|
namespace: "hwlab-ci",
|
|
labels: {
|
|
"app.kubernetes.io/part-of": "hwlab",
|
|
"hwlab.pikastech.local/gitops-target": settings.gitopsTarget,
|
|
"hwlab.pikastech.local/source-commit": source.full
|
|
}
|
|
},
|
|
spec: {
|
|
pipelineRef: { name: settings.pipelineName },
|
|
taskRunTemplate: {
|
|
serviceAccountName: settings.serviceAccountName,
|
|
podTemplate: {
|
|
hostNetwork: true,
|
|
dnsPolicy: "ClusterFirstWithHostNet",
|
|
securityContext: { fsGroup: 1000 }
|
|
}
|
|
},
|
|
params: [
|
|
{ name: "git-url", value: args.sourceRepo },
|
|
{ name: "git-read-url", value: args.gitReadUrl },
|
|
{ name: "git-write-url", value: args.gitWriteUrl },
|
|
{ name: "source-branch", value: args.sourceBranch },
|
|
{ name: "gitops-branch", value: args.gitopsBranch },
|
|
{ name: "lane", value: settings.lane },
|
|
{ name: "catalog-path", value: args.catalogPath || settings.catalogPath },
|
|
{ name: "image-tag-mode", value: args.imageTagMode || settings.imageTagMode },
|
|
{ name: "runtime-path", value: runtimePath },
|
|
{ name: "revision", value: source.full },
|
|
{ name: "registry-prefix", value: args.registryPrefix },
|
|
{ name: "base-image", value: defaultDevBaseImage },
|
|
{ name: "build-cache-mode", value: "registry" }
|
|
],
|
|
workspaces: [
|
|
{ name: "source", volumeClaimTemplate: { spec: { accessModes: ["ReadWriteOnce"], resources: { requests: { storage: "8Gi" } } } } },
|
|
{ name: "git-ssh", secret: { secretName: "hwlab-git-ssh" } }
|
|
]
|
|
}
|
|
};
|
|
}
|
|
|
|
function tektonPollerCronJob(args, deploy) {
|
|
const settings = ciLaneSettings(args);
|
|
if (settings.lane === "v02") throw new Error("v02 CI/CD is manually triggered by UniDesk CLI and must not render a branch poller CronJob");
|
|
return {
|
|
apiVersion: "batch/v1",
|
|
kind: "CronJob",
|
|
metadata: {
|
|
name: settings.pollerName,
|
|
namespace: "hwlab-ci",
|
|
labels: {
|
|
"app.kubernetes.io/part-of": "hwlab",
|
|
"hwlab.pikastech.local/gitops-target": settings.gitopsTarget
|
|
},
|
|
annotations: {
|
|
"hwlab.pikastech.local/source-branch": args.sourceBranch,
|
|
"hwlab.pikastech.local/gitops-branch": args.gitopsBranch
|
|
}
|
|
},
|
|
spec: {
|
|
schedule: settings.lane === "v02" ? "*/10 * * * *" : "* * * * *",
|
|
concurrencyPolicy: "Forbid",
|
|
successfulJobsHistoryLimit: 3,
|
|
failedJobsHistoryLimit: 3,
|
|
jobTemplate: {
|
|
spec: {
|
|
backoffLimit: 1,
|
|
template: {
|
|
metadata: {
|
|
labels: {
|
|
"app.kubernetes.io/part-of": "hwlab",
|
|
"hwlab.pikastech.local/gitops-target": settings.gitopsTarget
|
|
}
|
|
},
|
|
spec: {
|
|
serviceAccountName: settings.serviceAccountName,
|
|
restartPolicy: "Never",
|
|
hostNetwork: true,
|
|
dnsPolicy: "ClusterFirstWithHostNet",
|
|
containers: [{
|
|
name: "poll",
|
|
image: ciToolsRunnerImage,
|
|
imagePullPolicy: "IfNotPresent",
|
|
env: [
|
|
...proxyEnv(),
|
|
{ name: "POD_NAMESPACE", valueFrom: { fieldRef: { fieldPath: "metadata.namespace" } } },
|
|
{ name: "PIPELINE_NAME", value: settings.pipelineName },
|
|
{ name: "PIPELINERUN_PREFIX", value: settings.pipelineRunPrefix },
|
|
{ name: "SERVICE_ACCOUNT_NAME", value: settings.serviceAccountName },
|
|
{ name: "GITOPS_TARGET", value: settings.gitopsTarget },
|
|
{ name: "LANE", value: settings.lane },
|
|
{ name: "CATALOG_PATH", value: args.catalogPath || settings.catalogPath },
|
|
{ name: "IMAGE_TAG_MODE", value: args.imageTagMode || settings.imageTagMode },
|
|
{ name: "RUNTIME_PATH", value: gitopsPathForProfile(args, settings.profile) },
|
|
{ name: "GIT_URL", value: args.sourceRepo },
|
|
{ name: "GIT_READ_URL", value: args.gitReadUrl },
|
|
{ name: "SOURCE_BRANCH", value: args.sourceBranch },
|
|
{ name: "GITOPS_BRANCH", value: args.gitopsBranch },
|
|
{ name: "REGISTRY_PREFIX", value: args.registryPrefix },
|
|
{ name: "SERVICES", value: servicesParamForLane(args.lane, deploy) },
|
|
{ name: "BASE_IMAGE", value: defaultDevBaseImage }
|
|
],
|
|
volumeMounts: [{ name: "git-ssh", mountPath: "/workspace/git-ssh", readOnly: true }],
|
|
command: ["/bin/sh", "-c"],
|
|
args: [pollerScript()]
|
|
}],
|
|
volumes: [{ name: "git-ssh", secret: { secretName: "hwlab-git-ssh" } }]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function tektonControlPlaneReconcilerCronJob(args) {
|
|
const settings = ciLaneSettings(args);
|
|
if (settings.lane === "v02") throw new Error("v02 CI/CD is manually triggered by UniDesk CLI and must not render a control-plane reconciler CronJob");
|
|
return {
|
|
apiVersion: "batch/v1",
|
|
kind: "CronJob",
|
|
metadata: {
|
|
name: settings.controlPlaneReconcilerName,
|
|
namespace: "hwlab-ci",
|
|
labels: {
|
|
"app.kubernetes.io/part-of": "hwlab",
|
|
"hwlab.pikastech.local/gitops-target": settings.gitopsTarget
|
|
},
|
|
annotations: {
|
|
"hwlab.pikastech.local/source-branch": args.sourceBranch,
|
|
"hwlab.pikastech.local/gitops-branch": args.gitopsBranch,
|
|
"hwlab.pikastech.local/purpose": "render-and-apply-node-ci-control-plane"
|
|
}
|
|
},
|
|
spec: {
|
|
schedule: "*/10 * * * *",
|
|
concurrencyPolicy: "Forbid",
|
|
successfulJobsHistoryLimit: 3,
|
|
failedJobsHistoryLimit: 3,
|
|
jobTemplate: {
|
|
spec: {
|
|
backoffLimit: 1,
|
|
template: {
|
|
metadata: {
|
|
labels: {
|
|
"app.kubernetes.io/part-of": "hwlab",
|
|
"hwlab.pikastech.local/gitops-target": settings.gitopsTarget
|
|
}
|
|
},
|
|
spec: {
|
|
serviceAccountName: settings.serviceAccountName,
|
|
restartPolicy: "Never",
|
|
hostNetwork: true,
|
|
dnsPolicy: "ClusterFirstWithHostNet",
|
|
containers: [{
|
|
name: "reconcile",
|
|
image: ciToolsRunnerImage,
|
|
imagePullPolicy: "IfNotPresent",
|
|
env: [
|
|
...proxyEnv(),
|
|
{ name: "POD_NAMESPACE", valueFrom: { fieldRef: { fieldPath: "metadata.namespace" } } },
|
|
{ name: "LANE", value: settings.lane },
|
|
{ name: "CATALOG_PATH", value: args.catalogPath || settings.catalogPath },
|
|
{ name: "IMAGE_TAG_MODE", value: args.imageTagMode || settings.imageTagMode },
|
|
{ name: "TEKTON_DIR", value: settings.tektonDir },
|
|
{ name: "ARGO_FILES", value: settings.lane === "v02" ? `${gitopsPathFor(args, "argocd/project.yaml")},${gitopsPathFor(args, "argocd/application-v02.yaml")}` : "" },
|
|
{ name: "FIELD_MANAGER", value: settings.controlPlaneReconcilerName },
|
|
{ name: "RECONCILE_EVENT", value: `${settings.gitopsTarget}-control-plane-reconciled` },
|
|
{ name: "GIT_URL", value: args.sourceRepo },
|
|
{ name: "GIT_READ_URL", value: args.gitReadUrl },
|
|
{ name: "SOURCE_BRANCH", value: args.sourceBranch },
|
|
{ name: "GITOPS_BRANCH", value: args.gitopsBranch },
|
|
{ name: "REGISTRY_PREFIX", value: args.registryPrefix }
|
|
],
|
|
volumeMounts: [{ name: "git-ssh", mountPath: "/workspace/git-ssh", readOnly: true }],
|
|
command: ["/bin/sh", "-c"],
|
|
args: [controlPlaneReconcileScript()]
|
|
}],
|
|
volumes: [{ name: "git-ssh", secret: { secretName: "hwlab-git-ssh" } }]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
export {
|
|
affectedResultName,
|
|
buildResultName,
|
|
buildTaskName,
|
|
buildkitBinVolume,
|
|
buildkitRunVolume,
|
|
buildkitSidecar,
|
|
ciLaneSettings,
|
|
collectArtifactsTask,
|
|
imagePublishTaskSet,
|
|
perServiceBuildTask,
|
|
planArtifactsTask,
|
|
prepareBuildkitClientStep,
|
|
runtimeReadyScript,
|
|
runtimeReadyTask,
|
|
serviceResultEnv,
|
|
serviceResultEnvName,
|
|
serviceResultParamName,
|
|
serviceResultParams,
|
|
serviceResultParamValues,
|
|
serviceWorkVolume,
|
|
tektonControlPlaneReconcilerCronJob,
|
|
tektonPipeline,
|
|
tektonPipelineRunTemplate,
|
|
tektonPollerCronJob,
|
|
tektonRbac
|
|
};
|