Skip to content

Commit

Permalink
feat(Gitea): add support for actions [WIP]
Browse files Browse the repository at this point in the history
  • Loading branch information
kosmoz committed Nov 20, 2023
1 parent 43cbcdf commit 7e29812
Show file tree
Hide file tree
Showing 12 changed files with 315 additions and 3 deletions.
18 changes: 18 additions & 0 deletions deploy/crd/giteas.glasskube.eu-v1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,24 @@ spec:
required:
- s3
type: object
actions:
properties:
enabled:
type: boolean
runners:
items:
properties:
token:
type: string
labels:
items:
type: string
type: array
required:
- token
type: object
type: array
type: object
required:
- host
type: object
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package eu.glasskube.kubernetes.api.model.apps

import eu.glasskube.kubernetes.api.annotation.KubernetesDslMarker
import eu.glasskube.kubernetes.api.model.intOrString
import io.fabric8.kubernetes.api.model.apps.RollingUpdateStatefulSetStrategy
import io.fabric8.kubernetes.api.model.apps.RollingUpdateStatefulSetStrategyBuilder

@KubernetesDslMarker
class RollingUpdateStatefulSetStrategyDsl private constructor() {
private val builder = RollingUpdateStatefulSetStrategyBuilder(true)

Expand Down
12 changes: 12 additions & 0 deletions operator/src/main/kotlin/eu/glasskube/operator/apps/gitea/Gitea.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ class Gitea :
override fun getDatabaseName(primary: Gitea) = "gitea"
}

object Runner {
internal const val APP_NAME = "act-runner"
internal const val APP_VERSION = "0.2.6"
internal const val APP_IMAGE = "${Gitea.APP_NAME}/act_runner:$APP_VERSION"
internal const val DOCKER_IMAGE = "docker:23.0.6-dind"
}

@delegate:JsonIgnore
override val velero by lazy {
object : VeleroNameMapper(this) {
Expand All @@ -59,6 +66,8 @@ class Gitea :
}
}

internal const val GITEA_RUNNER_LABEL = "glasskube.eu/gitea-runner"

val Gitea.resourceLabels
get() = Labels.resourceLabels(Gitea.APP_NAME, metadata.name, Gitea.APP_NAME, spec.version)
val Gitea.resourceLabelSelector
Expand All @@ -71,3 +80,6 @@ val Gitea.iniConfigMapName get() = "$genericResourceName-ini"
val Gitea.httpServiceName get() = "$genericResourceName-http"
val Gitea.sshServiceName get() = "$genericResourceName-ssh"
val Gitea.ingressTlsCertName get() = "$genericResourceName-cert"
fun Gitea.getRunnerName(runner: GiteaActionRunnerSpecTemplate) = "$genericResourceName-runner-${runner.tokenHash}"
val GiteaActionRunnerSpecTemplate.resourceLabels
get() = mapOf(Labels.COMPONENT to Gitea.Runner.APP_NAME, GITEA_RUNNER_LABEL to tokenHash)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package eu.glasskube.operator.apps.gitea

import eu.glasskube.utils.resourceHash
import io.fabric8.generator.annotation.Required

data class GiteaActionRunnerSpecTemplate(
@field:Required
val token: String,
val labels: List<String>? = null
)

val GiteaActionRunnerSpecTemplate.tokenHash: String
get() = token.resourceHash
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package eu.glasskube.operator.apps.gitea

data class GiteaActionsSpec(
val enabled: Boolean = false,
val runners: List<GiteaActionRunnerSpecTemplate> = emptyList()
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import eu.glasskube.kubernetes.client.patchOrUpdateStatus
import eu.glasskube.operator.Labels
import eu.glasskube.operator.api.reconciler.informerEventSource
import eu.glasskube.operator.api.reconciler.secondaryResource
import eu.glasskube.operator.apps.gitea.dependent.GiteaActionRunnerSecrets
import eu.glasskube.operator.apps.gitea.dependent.GiteaActionRunnerStatefulSets
import eu.glasskube.operator.apps.gitea.dependent.GiteaConfigMap
import eu.glasskube.operator.apps.gitea.dependent.GiteaDeployment
import eu.glasskube.operator.apps.gitea.dependent.GiteaHttpService
Expand Down Expand Up @@ -85,7 +87,8 @@ import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers
type = GiteaDeployment::class,
name = "GiteaDeployment",
dependsOn = ["GiteaPostgresCluster", "GiteaVolume", "GiteaSecret", "GiteaConfigMap", "GiteaIniConfigMap", "GiteaRedisService"],
useEventSourceWithName = GiteaReconciler.DEPLOYMENT_EVENT_SOURCE
useEventSourceWithName = GiteaReconciler.DEPLOYMENT_EVENT_SOURCE,
readyPostcondition = GiteaDeployment.ReadyCondition::class
),
Dependent(
type = GiteaHttpService::class,
Expand All @@ -107,6 +110,16 @@ import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers
name = "GiteaServiceMonitor",
dependsOn = ["GiteaHttpService"]
),
Dependent(
type = GiteaActionRunnerStatefulSets::class,
name = "GiteaActionRunnerStatefulSets",
dependsOn = ["GiteaDeployment"]
),
Dependent(
type = GiteaActionRunnerSecrets::class,
name = "GiteaActionRunnerSecrets",
dependsOn = ["GiteaDeployment"]
),
Dependent(
type = GiteaVeleroSecret::class,
name = "GiteaVeleroSecret",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ data class GiteaSpec(
val version: String = "1.20.4",
@field:Nullable
override val database: PostgresDatabaseSpec = PostgresDatabaseSpec(),
override val backups: BackupSpec?
override val backups: BackupSpec?,
val actions: GiteaActionsSpec = GiteaActionsSpec()
) : HasBackupSpec, HasDatabaseSpec<PostgresDatabaseSpec>
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package eu.glasskube.operator.apps.gitea.dependent

import eu.glasskube.kubernetes.api.model.metadata
import eu.glasskube.kubernetes.api.model.namespace
import eu.glasskube.kubernetes.api.model.secret
import eu.glasskube.operator.apps.gitea.GITEA_RUNNER_LABEL
import eu.glasskube.operator.apps.gitea.Gitea
import eu.glasskube.operator.apps.gitea.GiteaReconciler
import eu.glasskube.operator.apps.gitea.getRunnerName
import eu.glasskube.operator.apps.gitea.resourceLabels
import eu.glasskube.utils.encodeBase64
import io.fabric8.kubernetes.api.model.Secret
import io.javaoperatorsdk.operator.api.reconciler.Context
import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource
import io.javaoperatorsdk.operator.processing.dependent.Matcher
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent

@KubernetesDependent(labelSelector = GiteaReconciler.SELECTOR)
class GiteaActionRunnerSecrets :
CRUDKubernetesDependentResource<Secret, Gitea>(Secret::class.java), BulkDependentResource<Secret, Gitea> {
override fun desiredResources(primary: Gitea, context: Context<Gitea>) =
primary.spec.actions.runners
.map {
secret {
metadata {
name(primary.getRunnerName(it))
namespace(primary.namespace)
labels(primary.resourceLabels + it.resourceLabels)
}
data = mapOf("GITEA_RUNNER_REGISTRATION_TOKEN" to it.token.encodeBase64())
}
}
.associateBy { it.metadata.name }

override fun getSecondaryResources(primary: Gitea, context: Context<Gitea>) =
context.getSecondaryResources(Secret::class.java)
.filter { GITEA_RUNNER_LABEL in it.metadata.labels }
.associateBy { it.metadata.name }
.toMutableMap()

override fun match(
actualResource: Secret,
desired: Secret,
primary: Gitea,
context: Context<Gitea>
): Matcher.Result<Secret> =
super<CRUDKubernetesDependentResource>.match(actualResource, desired, primary, context)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package eu.glasskube.operator.apps.gitea.dependent

import eu.glasskube.kubernetes.api.model.apps.statefulSet
import eu.glasskube.kubernetes.api.model.container
import eu.glasskube.kubernetes.api.model.emptyDir
import eu.glasskube.kubernetes.api.model.env
import eu.glasskube.kubernetes.api.model.envFrom
import eu.glasskube.kubernetes.api.model.envVar
import eu.glasskube.kubernetes.api.model.limits
import eu.glasskube.kubernetes.api.model.metadata
import eu.glasskube.kubernetes.api.model.namespace
import eu.glasskube.kubernetes.api.model.requests
import eu.glasskube.kubernetes.api.model.resources
import eu.glasskube.kubernetes.api.model.secretRef
import eu.glasskube.kubernetes.api.model.securityContext
import eu.glasskube.kubernetes.api.model.spec
import eu.glasskube.kubernetes.api.model.volume
import eu.glasskube.kubernetes.api.model.volumeMount
import eu.glasskube.kubernetes.api.model.volumeMounts
import eu.glasskube.operator.apps.gitea.Gitea
import eu.glasskube.operator.apps.gitea.GiteaReconciler
import eu.glasskube.operator.apps.gitea.getRunnerName
import eu.glasskube.operator.apps.gitea.resourceLabelSelector
import eu.glasskube.operator.apps.gitea.resourceLabels
import io.fabric8.kubernetes.api.model.Quantity
import io.fabric8.kubernetes.api.model.apps.StatefulSet
import io.javaoperatorsdk.operator.api.reconciler.Context
import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource
import io.javaoperatorsdk.operator.processing.dependent.Matcher
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent

@KubernetesDependent(labelSelector = GiteaReconciler.SELECTOR)
class GiteaActionRunnerStatefulSets :
CRUDKubernetesDependentResource<StatefulSet, Gitea>(StatefulSet::class.java), BulkDependentResource<StatefulSet, Gitea> {
override fun desiredResources(primary: Gitea, context: Context<Gitea>) =
primary.spec.actions.runners
.map {
statefulSet {
metadata {
name(primary.getRunnerName(it))
namespace(primary.namespace)
labels(primary.resourceLabels + it.resourceLabels)
}
spec {
selector { matchLabels = primary.resourceLabelSelector + it.resourceLabels }
replicas(1)
updateStrategyRollingUpdate {
maxUnavailable("100%")
}
volumeClaimTemplates {
volumeClaimTemplate {
metadata { name(RUNNER_DATA_VOLUME) }
spec {
resources { requests = mapOf("storage" to Quantity("5", "Gi")) }
accessModes = listOf("ReadWriteOnce")
}
}
}
template {
metadata { labels(primary.resourceLabels + it.resourceLabels) }
spec {
volumes = listOf(
volume(DOCKER_CERT_VOLUME) { emptyDir() }
)
containers = listOf(
container {
name = Gitea.Runner.APP_NAME
image = Gitea.Runner.APP_IMAGE
command = listOf("sh")
args = listOf(
"-c",
"while ! nc -z localhost 2376 </dev/null; do echo 'waiting for docker daemon...'; sleep 5; done; /sbin/tini -- /opt/act/run.sh"
)
env {
envVar("DOCKER_HOST", DOCKER_HOST)
envVar("DOCKER_CERT_PATH", DOCKER_CLIENT_CERT_PATH)
envVar("DOCKER_TLS_VERIFY", "1")
envVar("GITEA_INSTANCE_URL", "http://gitea-${primary.metadata.name}-http:3000")
it.labels?.takeIf { it.isNotEmpty() }?.also {
envVar("GITEA_RUNNER_LABELS", it.joinToString(","))
}
}
envFrom { secretRef(primary.getRunnerName(it)) }
volumeMounts {
volumeMount {
name = DOCKER_CERT_VOLUME
mountPath = DOCKER_CERT_PATH
}
volumeMount {
name = RUNNER_DATA_VOLUME
mountPath = RUNNER_DATA_PATH
subPath = "data"
}
}
resources {
requests(
cpu = Quantity("200", "m"),
memory = Quantity("250", "Mi")
)
limits(
cpu = Quantity("200", "m")
)
}
},
container {
name = "docker"
image = Gitea.Runner.DOCKER_IMAGE
env { envVar("DOCKER_TLS_CERTDIR", DOCKER_CERT_PATH) }
volumeMounts {
volumeMount {
name = DOCKER_CERT_VOLUME
mountPath = DOCKER_CERT_PATH
}
volumeMount {
name = RUNNER_DATA_VOLUME
mountPath = DOCKER_DATA_PATH
subPath = "docker"
}
}
securityContext { privileged = true }
resources {
requests(
cpu = Quantity("1"),
memory = Quantity("1", "Gi")
)
limits(
cpu = Quantity("1")
)
}
}
)
}
}
}
}
}
.associateBy { it.metadata.name }

override fun getSecondaryResources(primary: Gitea, context: Context<Gitea>) =
context.getSecondaryResources(StatefulSet::class.java)
.associateBy { it.metadata.name }
.toMutableMap()

override fun match(
actualResource: StatefulSet,
desired: StatefulSet,
primary: Gitea,
context: Context<Gitea>
): Matcher.Result<StatefulSet> =
super<BulkDependentResource>.match(actualResource, desired, primary, context)

companion object {
private const val DOCKER_HOST = "tcp://localhost:2376"
private const val DOCKER_CERT_PATH = "/certs"
private const val DOCKER_CLIENT_CERT_PATH = "$DOCKER_CERT_PATH/client"
private const val DOCKER_CERT_VOLUME = "docker-certs"
private const val RUNNER_DATA_PATH = "/data"
private const val RUNNER_DATA_VOLUME = "runner-data"
private const val DOCKER_DATA_PATH = "/var/lib/docker"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import eu.glasskube.operator.apps.gitea.resourceLabelSelector
import eu.glasskube.operator.apps.gitea.resourceLabels
import eu.glasskube.operator.apps.gitea.secretName
import eu.glasskube.operator.config.ConfigService
import eu.glasskube.operator.generic.condition.DeploymentReadyCondition
import eu.glasskube.utils.addTo
import io.fabric8.kubernetes.api.model.HTTPGetAction
import io.fabric8.kubernetes.api.model.Probe
Expand All @@ -52,6 +53,7 @@ class GiteaDeployment(private val configService: ConfigService) :
CRUDKubernetesDependentResource<Deployment, Gitea>(Deployment::class.java) {
internal class Discriminator :
ResourceIDMatcherDiscriminator<Deployment, Gitea>({ ResourceID(it.deploymentName, it.namespace) })
internal class ReadyCondition : DeploymentReadyCondition<Gitea>()

override fun desired(primary: Gitea, context: Context<Gitea>) = deployment {
metadata {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class GiteaIniConfigMap : CRUDKubernetesDependentResource<ConfigMap, Gitea>(Conf
namespace(primary.namespace)
labels(primary.resourceLabels)
}
data = primary.baseConfig + getSmtpConfig(primary, context)
data = primary.baseConfig + getSmtpConfig(primary, context) + primary.actionsConfig
}

private val Gitea.baseConfig: Map<String, String>
Expand Down Expand Up @@ -87,6 +87,11 @@ class GiteaIniConfigMap : CRUDKubernetesDependentResource<ConfigMap, Gitea>(Conf
}
}

private val Gitea.actionsConfig: Map<String, String>
get() = mapOf(
"GITEA__actions__ENABLED" to spec.actions.enabled.toString()
)

override fun onUpdated(primary: Gitea, updated: ConfigMap, actual: ConfigMap, context: Context<Gitea>) {
super.onUpdated(primary, updated, actual, context)
context.getSecondaryResource(GiteaDeployment.Discriminator()).ifPresent {
Expand Down
Loading

0 comments on commit 7e29812

Please sign in to comment.