Skip to content

Commit 7e29812

Browse files
committed
feat(Gitea): add support for actions [WIP]
1 parent 43cbcdf commit 7e29812

File tree

12 files changed

+315
-3
lines changed

12 files changed

+315
-3
lines changed

deploy/crd/giteas.glasskube.eu-v1.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,24 @@ spec:
193193
required:
194194
- s3
195195
type: object
196+
actions:
197+
properties:
198+
enabled:
199+
type: boolean
200+
runners:
201+
items:
202+
properties:
203+
token:
204+
type: string
205+
labels:
206+
items:
207+
type: string
208+
type: array
209+
required:
210+
- token
211+
type: object
212+
type: array
213+
type: object
196214
required:
197215
- host
198216
type: object

operator/src/main/kotlin/eu/glasskube/kubernetes/api/model/apps/RollingUpdateStatefulSetStrategyDsl.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package eu.glasskube.kubernetes.api.model.apps
22

3+
import eu.glasskube.kubernetes.api.annotation.KubernetesDslMarker
34
import eu.glasskube.kubernetes.api.model.intOrString
45
import io.fabric8.kubernetes.api.model.apps.RollingUpdateStatefulSetStrategy
56
import io.fabric8.kubernetes.api.model.apps.RollingUpdateStatefulSetStrategyBuilder
67

8+
@KubernetesDslMarker
79
class RollingUpdateStatefulSetStrategyDsl private constructor() {
810
private val builder = RollingUpdateStatefulSetStrategyBuilder(true)
911

operator/src/main/kotlin/eu/glasskube/operator/apps/gitea/Gitea.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ class Gitea :
4949
override fun getDatabaseName(primary: Gitea) = "gitea"
5050
}
5151

52+
object Runner {
53+
internal const val APP_NAME = "act-runner"
54+
internal const val APP_VERSION = "0.2.6"
55+
internal const val APP_IMAGE = "${Gitea.APP_NAME}/act_runner:$APP_VERSION"
56+
internal const val DOCKER_IMAGE = "docker:23.0.6-dind"
57+
}
58+
5259
@delegate:JsonIgnore
5360
override val velero by lazy {
5461
object : VeleroNameMapper(this) {
@@ -59,6 +66,8 @@ class Gitea :
5966
}
6067
}
6168

69+
internal const val GITEA_RUNNER_LABEL = "glasskube.eu/gitea-runner"
70+
6271
val Gitea.resourceLabels
6372
get() = Labels.resourceLabels(Gitea.APP_NAME, metadata.name, Gitea.APP_NAME, spec.version)
6473
val Gitea.resourceLabelSelector
@@ -71,3 +80,6 @@ val Gitea.iniConfigMapName get() = "$genericResourceName-ini"
7180
val Gitea.httpServiceName get() = "$genericResourceName-http"
7281
val Gitea.sshServiceName get() = "$genericResourceName-ssh"
7382
val Gitea.ingressTlsCertName get() = "$genericResourceName-cert"
83+
fun Gitea.getRunnerName(runner: GiteaActionRunnerSpecTemplate) = "$genericResourceName-runner-${runner.tokenHash}"
84+
val GiteaActionRunnerSpecTemplate.resourceLabels
85+
get() = mapOf(Labels.COMPONENT to Gitea.Runner.APP_NAME, GITEA_RUNNER_LABEL to tokenHash)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package eu.glasskube.operator.apps.gitea
2+
3+
import eu.glasskube.utils.resourceHash
4+
import io.fabric8.generator.annotation.Required
5+
6+
data class GiteaActionRunnerSpecTemplate(
7+
@field:Required
8+
val token: String,
9+
val labels: List<String>? = null
10+
)
11+
12+
val GiteaActionRunnerSpecTemplate.tokenHash: String
13+
get() = token.resourceHash
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package eu.glasskube.operator.apps.gitea
2+
3+
data class GiteaActionsSpec(
4+
val enabled: Boolean = false,
5+
val runners: List<GiteaActionRunnerSpecTemplate> = emptyList()
6+
)

operator/src/main/kotlin/eu/glasskube/operator/apps/gitea/GiteaReconciler.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import eu.glasskube.kubernetes.client.patchOrUpdateStatus
44
import eu.glasskube.operator.Labels
55
import eu.glasskube.operator.api.reconciler.informerEventSource
66
import eu.glasskube.operator.api.reconciler.secondaryResource
7+
import eu.glasskube.operator.apps.gitea.dependent.GiteaActionRunnerSecrets
8+
import eu.glasskube.operator.apps.gitea.dependent.GiteaActionRunnerStatefulSets
79
import eu.glasskube.operator.apps.gitea.dependent.GiteaConfigMap
810
import eu.glasskube.operator.apps.gitea.dependent.GiteaDeployment
911
import eu.glasskube.operator.apps.gitea.dependent.GiteaHttpService
@@ -85,7 +87,8 @@ import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers
8587
type = GiteaDeployment::class,
8688
name = "GiteaDeployment",
8789
dependsOn = ["GiteaPostgresCluster", "GiteaVolume", "GiteaSecret", "GiteaConfigMap", "GiteaIniConfigMap", "GiteaRedisService"],
88-
useEventSourceWithName = GiteaReconciler.DEPLOYMENT_EVENT_SOURCE
90+
useEventSourceWithName = GiteaReconciler.DEPLOYMENT_EVENT_SOURCE,
91+
readyPostcondition = GiteaDeployment.ReadyCondition::class
8992
),
9093
Dependent(
9194
type = GiteaHttpService::class,
@@ -107,6 +110,16 @@ import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers
107110
name = "GiteaServiceMonitor",
108111
dependsOn = ["GiteaHttpService"]
109112
),
113+
Dependent(
114+
type = GiteaActionRunnerStatefulSets::class,
115+
name = "GiteaActionRunnerStatefulSets",
116+
dependsOn = ["GiteaDeployment"]
117+
),
118+
Dependent(
119+
type = GiteaActionRunnerSecrets::class,
120+
name = "GiteaActionRunnerSecrets",
121+
dependsOn = ["GiteaDeployment"]
122+
),
110123
Dependent(
111124
type = GiteaVeleroSecret::class,
112125
name = "GiteaVeleroSecret",

operator/src/main/kotlin/eu/glasskube/operator/apps/gitea/GiteaSpec.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@ data class GiteaSpec(
3333
val version: String = "1.20.4",
3434
@field:Nullable
3535
override val database: PostgresDatabaseSpec = PostgresDatabaseSpec(),
36-
override val backups: BackupSpec?
36+
override val backups: BackupSpec?,
37+
val actions: GiteaActionsSpec = GiteaActionsSpec()
3738
) : HasBackupSpec, HasDatabaseSpec<PostgresDatabaseSpec>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package eu.glasskube.operator.apps.gitea.dependent
2+
3+
import eu.glasskube.kubernetes.api.model.metadata
4+
import eu.glasskube.kubernetes.api.model.namespace
5+
import eu.glasskube.kubernetes.api.model.secret
6+
import eu.glasskube.operator.apps.gitea.GITEA_RUNNER_LABEL
7+
import eu.glasskube.operator.apps.gitea.Gitea
8+
import eu.glasskube.operator.apps.gitea.GiteaReconciler
9+
import eu.glasskube.operator.apps.gitea.getRunnerName
10+
import eu.glasskube.operator.apps.gitea.resourceLabels
11+
import eu.glasskube.utils.encodeBase64
12+
import io.fabric8.kubernetes.api.model.Secret
13+
import io.javaoperatorsdk.operator.api.reconciler.Context
14+
import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource
15+
import io.javaoperatorsdk.operator.processing.dependent.Matcher
16+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource
17+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent
18+
19+
@KubernetesDependent(labelSelector = GiteaReconciler.SELECTOR)
20+
class GiteaActionRunnerSecrets :
21+
CRUDKubernetesDependentResource<Secret, Gitea>(Secret::class.java), BulkDependentResource<Secret, Gitea> {
22+
override fun desiredResources(primary: Gitea, context: Context<Gitea>) =
23+
primary.spec.actions.runners
24+
.map {
25+
secret {
26+
metadata {
27+
name(primary.getRunnerName(it))
28+
namespace(primary.namespace)
29+
labels(primary.resourceLabels + it.resourceLabels)
30+
}
31+
data = mapOf("GITEA_RUNNER_REGISTRATION_TOKEN" to it.token.encodeBase64())
32+
}
33+
}
34+
.associateBy { it.metadata.name }
35+
36+
override fun getSecondaryResources(primary: Gitea, context: Context<Gitea>) =
37+
context.getSecondaryResources(Secret::class.java)
38+
.filter { GITEA_RUNNER_LABEL in it.metadata.labels }
39+
.associateBy { it.metadata.name }
40+
.toMutableMap()
41+
42+
override fun match(
43+
actualResource: Secret,
44+
desired: Secret,
45+
primary: Gitea,
46+
context: Context<Gitea>
47+
): Matcher.Result<Secret> =
48+
super<CRUDKubernetesDependentResource>.match(actualResource, desired, primary, context)
49+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package eu.glasskube.operator.apps.gitea.dependent
2+
3+
import eu.glasskube.kubernetes.api.model.apps.statefulSet
4+
import eu.glasskube.kubernetes.api.model.container
5+
import eu.glasskube.kubernetes.api.model.emptyDir
6+
import eu.glasskube.kubernetes.api.model.env
7+
import eu.glasskube.kubernetes.api.model.envFrom
8+
import eu.glasskube.kubernetes.api.model.envVar
9+
import eu.glasskube.kubernetes.api.model.limits
10+
import eu.glasskube.kubernetes.api.model.metadata
11+
import eu.glasskube.kubernetes.api.model.namespace
12+
import eu.glasskube.kubernetes.api.model.requests
13+
import eu.glasskube.kubernetes.api.model.resources
14+
import eu.glasskube.kubernetes.api.model.secretRef
15+
import eu.glasskube.kubernetes.api.model.securityContext
16+
import eu.glasskube.kubernetes.api.model.spec
17+
import eu.glasskube.kubernetes.api.model.volume
18+
import eu.glasskube.kubernetes.api.model.volumeMount
19+
import eu.glasskube.kubernetes.api.model.volumeMounts
20+
import eu.glasskube.operator.apps.gitea.Gitea
21+
import eu.glasskube.operator.apps.gitea.GiteaReconciler
22+
import eu.glasskube.operator.apps.gitea.getRunnerName
23+
import eu.glasskube.operator.apps.gitea.resourceLabelSelector
24+
import eu.glasskube.operator.apps.gitea.resourceLabels
25+
import io.fabric8.kubernetes.api.model.Quantity
26+
import io.fabric8.kubernetes.api.model.apps.StatefulSet
27+
import io.javaoperatorsdk.operator.api.reconciler.Context
28+
import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource
29+
import io.javaoperatorsdk.operator.processing.dependent.Matcher
30+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource
31+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent
32+
33+
@KubernetesDependent(labelSelector = GiteaReconciler.SELECTOR)
34+
class GiteaActionRunnerStatefulSets :
35+
CRUDKubernetesDependentResource<StatefulSet, Gitea>(StatefulSet::class.java), BulkDependentResource<StatefulSet, Gitea> {
36+
override fun desiredResources(primary: Gitea, context: Context<Gitea>) =
37+
primary.spec.actions.runners
38+
.map {
39+
statefulSet {
40+
metadata {
41+
name(primary.getRunnerName(it))
42+
namespace(primary.namespace)
43+
labels(primary.resourceLabels + it.resourceLabels)
44+
}
45+
spec {
46+
selector { matchLabels = primary.resourceLabelSelector + it.resourceLabels }
47+
replicas(1)
48+
updateStrategyRollingUpdate {
49+
maxUnavailable("100%")
50+
}
51+
volumeClaimTemplates {
52+
volumeClaimTemplate {
53+
metadata { name(RUNNER_DATA_VOLUME) }
54+
spec {
55+
resources { requests = mapOf("storage" to Quantity("5", "Gi")) }
56+
accessModes = listOf("ReadWriteOnce")
57+
}
58+
}
59+
}
60+
template {
61+
metadata { labels(primary.resourceLabels + it.resourceLabels) }
62+
spec {
63+
volumes = listOf(
64+
volume(DOCKER_CERT_VOLUME) { emptyDir() }
65+
)
66+
containers = listOf(
67+
container {
68+
name = Gitea.Runner.APP_NAME
69+
image = Gitea.Runner.APP_IMAGE
70+
command = listOf("sh")
71+
args = listOf(
72+
"-c",
73+
"while ! nc -z localhost 2376 </dev/null; do echo 'waiting for docker daemon...'; sleep 5; done; /sbin/tini -- /opt/act/run.sh"
74+
)
75+
env {
76+
envVar("DOCKER_HOST", DOCKER_HOST)
77+
envVar("DOCKER_CERT_PATH", DOCKER_CLIENT_CERT_PATH)
78+
envVar("DOCKER_TLS_VERIFY", "1")
79+
envVar("GITEA_INSTANCE_URL", "http://gitea-${primary.metadata.name}-http:3000")
80+
it.labels?.takeIf { it.isNotEmpty() }?.also {
81+
envVar("GITEA_RUNNER_LABELS", it.joinToString(","))
82+
}
83+
}
84+
envFrom { secretRef(primary.getRunnerName(it)) }
85+
volumeMounts {
86+
volumeMount {
87+
name = DOCKER_CERT_VOLUME
88+
mountPath = DOCKER_CERT_PATH
89+
}
90+
volumeMount {
91+
name = RUNNER_DATA_VOLUME
92+
mountPath = RUNNER_DATA_PATH
93+
subPath = "data"
94+
}
95+
}
96+
resources {
97+
requests(
98+
cpu = Quantity("200", "m"),
99+
memory = Quantity("250", "Mi")
100+
)
101+
limits(
102+
cpu = Quantity("200", "m")
103+
)
104+
}
105+
},
106+
container {
107+
name = "docker"
108+
image = Gitea.Runner.DOCKER_IMAGE
109+
env { envVar("DOCKER_TLS_CERTDIR", DOCKER_CERT_PATH) }
110+
volumeMounts {
111+
volumeMount {
112+
name = DOCKER_CERT_VOLUME
113+
mountPath = DOCKER_CERT_PATH
114+
}
115+
volumeMount {
116+
name = RUNNER_DATA_VOLUME
117+
mountPath = DOCKER_DATA_PATH
118+
subPath = "docker"
119+
}
120+
}
121+
securityContext { privileged = true }
122+
resources {
123+
requests(
124+
cpu = Quantity("1"),
125+
memory = Quantity("1", "Gi")
126+
)
127+
limits(
128+
cpu = Quantity("1")
129+
)
130+
}
131+
}
132+
)
133+
}
134+
}
135+
}
136+
}
137+
}
138+
.associateBy { it.metadata.name }
139+
140+
override fun getSecondaryResources(primary: Gitea, context: Context<Gitea>) =
141+
context.getSecondaryResources(StatefulSet::class.java)
142+
.associateBy { it.metadata.name }
143+
.toMutableMap()
144+
145+
override fun match(
146+
actualResource: StatefulSet,
147+
desired: StatefulSet,
148+
primary: Gitea,
149+
context: Context<Gitea>
150+
): Matcher.Result<StatefulSet> =
151+
super<BulkDependentResource>.match(actualResource, desired, primary, context)
152+
153+
companion object {
154+
private const val DOCKER_HOST = "tcp://localhost:2376"
155+
private const val DOCKER_CERT_PATH = "/certs"
156+
private const val DOCKER_CLIENT_CERT_PATH = "$DOCKER_CERT_PATH/client"
157+
private const val DOCKER_CERT_VOLUME = "docker-certs"
158+
private const val RUNNER_DATA_PATH = "/data"
159+
private const val RUNNER_DATA_VOLUME = "runner-data"
160+
private const val DOCKER_DATA_PATH = "/var/lib/docker"
161+
}
162+
}

operator/src/main/kotlin/eu/glasskube/operator/apps/gitea/dependent/GiteaDeployment.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import eu.glasskube.operator.apps.gitea.resourceLabelSelector
3333
import eu.glasskube.operator.apps.gitea.resourceLabels
3434
import eu.glasskube.operator.apps.gitea.secretName
3535
import eu.glasskube.operator.config.ConfigService
36+
import eu.glasskube.operator.generic.condition.DeploymentReadyCondition
3637
import eu.glasskube.utils.addTo
3738
import io.fabric8.kubernetes.api.model.HTTPGetAction
3839
import io.fabric8.kubernetes.api.model.Probe
@@ -52,6 +53,7 @@ class GiteaDeployment(private val configService: ConfigService) :
5253
CRUDKubernetesDependentResource<Deployment, Gitea>(Deployment::class.java) {
5354
internal class Discriminator :
5455
ResourceIDMatcherDiscriminator<Deployment, Gitea>({ ResourceID(it.deploymentName, it.namespace) })
56+
internal class ReadyCondition : DeploymentReadyCondition<Gitea>()
5557

5658
override fun desired(primary: Gitea, context: Context<Gitea>) = deployment {
5759
metadata {

0 commit comments

Comments
 (0)