1
0
mirror of https://github.com/woodpecker-ci/woodpecker.git synced 2024-11-24 08:02:18 +02:00

Parse backend options in backend (#3227)

Currently, backend options are parsed in the yaml parser.
This has some issues:
- backend specific code should be in the backend folders
- it is not possible to add backend options for backends added via
addons
This commit is contained in:
qwerty287 2024-02-08 18:39:32 +01:00 committed by GitHub
parent d3b57ff0e6
commit 6892a9ca57
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 224 additions and 284 deletions

1
go.mod
View File

@ -32,6 +32,7 @@ require (
github.com/kinbiko/jsonassert v1.1.1
github.com/lib/pq v1.10.9
github.com/mattn/go-sqlite3 v1.14.20
github.com/mitchellh/mapstructure v1.4.2
github.com/moby/moby v24.0.9+incompatible
github.com/moby/term v0.5.0
github.com/muesli/termenv v0.15.2

29
go.sum
View File

@ -67,14 +67,10 @@ github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDror
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg=
github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v24.0.8+incompatible h1:nZJsIKYXLeYFpsskShljFQcImMXw7zt+3DN/Ay/A6SI=
github.com/docker/cli v24.0.8+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v24.0.8+incompatible h1:lbGlhLzolo0tpp+paD0JzOYId072MQmQxZLPevQCFPU=
github.com/docker/docker v24.0.8+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
@ -89,8 +85,6 @@ github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/expr-lang/expr v1.15.8 h1:FL8+d3rSSP4tmK9o+vKfSMqqpGL8n15pEPiHcnBpxoI=
github.com/expr-lang/expr v1.15.8/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ=
github.com/expr-lang/expr v1.16.0 h1:BQabx+PbjsL2PEQwkJ4GIn3CcuUh8flduHhJ0lHjWwE=
github.com/expr-lang/expr v1.16.0/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@ -149,8 +143,9 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -306,8 +301,6 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.20 h1:BAZ50Ns0OFBNxdAqFhbZqdPcht1Xlb16pDCqkq1spr0=
github.com/mattn/go-sqlite3 v1.14.20/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
@ -316,10 +309,8 @@ github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/moby/moby v24.0.7+incompatible h1:RrVT5IXBn85mRtFKP+gFwVLCcnNPZIgN3NVRJG9Le+4=
github.com/moby/moby v24.0.7+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
github.com/moby/moby v24.0.8+incompatible h1:lTOrmnT/ZwYrhTbcmkWMTd2Pk65vV+4YuEdIG04shac=
github.com/moby/moby v24.0.8+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo=
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/moby v24.0.9+incompatible h1:Z/hFbZJqC5Fmuf6jesMLdHU71CMAgdiSJ1ZYey+bFmg=
github.com/moby/moby v24.0.9+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
@ -429,8 +420,6 @@ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65E
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xanzy/go-gitlab v0.95.2 h1:4p0IirHqEp5f0baK/aQqr4TR57IsD+8e4fuyAA1yi88=
github.com/xanzy/go-gitlab v0.95.2/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
github.com/xanzy/go-gitlab v0.96.0 h1:LGkZ+wSNMRtHIBaYE4Hq3dZVjprwHv3Y1+rhKU3WETs=
github.com/xanzy/go-gitlab v0.96.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@ -604,8 +593,6 @@ google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAs
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA=
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
@ -636,16 +623,10 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
k8s.io/api v0.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A=
k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA=
k8s.io/api v0.29.1 h1:DAjwWX/9YT7NQD4INu49ROJuZAAAP/Ijki48GUPzxqw=
k8s.io/api v0.29.1/go.mod h1:7Kl10vBRUXhnQQI8YR/R327zXC8eJ7887/+Ybta+RoQ=
k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o=
k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis=
k8s.io/apimachinery v0.29.1 h1:KY4/E6km/wLBguvCZv8cKTeOwwOBqFNjwJIdMkMbbRc=
k8s.io/apimachinery v0.29.1/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8=
k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38=
k8s.io/client-go v0.29.1 h1:19B/+2NGEwnFLzt0uB5kNJnfTsbV8w6TgQRz9l7ti7A=
k8s.io/client-go v0.29.1/go.mod h1:TDG/psL9hdet0TI9mGyHJSgRkW3H9JZk2dNEUS7bRks=
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
@ -705,7 +686,5 @@ xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohF
xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo=
xorm.io/builder v0.3.13/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/xorm v1.3.3/go.mod h1:qFJGFoVYbbIdnz2vaL5OxSQ2raleMpyRRalnq3n9OJo=
xorm.io/xorm v1.3.6 h1:hfpWHkDIWWqUi8FRF2H2M9O8lO3Ov47rwFcS9gPzPkU=
xorm.io/xorm v1.3.6/go.mod h1:qFJGFoVYbbIdnz2vaL5OxSQ2raleMpyRRalnq3n9OJo=
xorm.io/xorm v1.3.7 h1:mLceAGu0b87r9pD4qXyxGHxifOXIIrAdVcA6k95/osw=
xorm.io/xorm v1.3.7/go.mod h1:LsCCffeeYp63ssk0pKumP6l96WZcHix7ChpurcLNuMw=

View File

@ -0,0 +1,77 @@
package kubernetes
import (
"github.com/mitchellh/mapstructure"
backend "go.woodpecker-ci.org/woodpecker/v2/pipeline/backend/types"
)
// BackendOptions defines all the advanced options for the kubernetes backend
type BackendOptions struct {
Resources Resources `mapstructure:"resources"`
ServiceAccountName string `mapstructure:"serviceAccountName"`
NodeSelector map[string]string `mapstructure:"nodeSelector"`
Tolerations []Toleration `mapstructure:"tolerations"`
SecurityContext *SecurityContext `mapstructure:"securityContext"`
}
// Resources defines two maps for kubernetes resource definitions
type Resources struct {
Requests map[string]string `mapstructure:"requests"`
Limits map[string]string `mapstructure:"limits"`
}
// Toleration defines Kubernetes toleration
type Toleration struct {
Key string `mapstructure:"key"`
Operator TolerationOperator `mapstructure:"operator"`
Value string `mapstructure:"value"`
Effect TaintEffect `mapstructure:"effect"`
TolerationSeconds *int64 `mapstructure:"tolerationSeconds"`
}
type TaintEffect string
const (
TaintEffectNoSchedule TaintEffect = "NoSchedule"
TaintEffectPreferNoSchedule TaintEffect = "PreferNoSchedule"
TaintEffectNoExecute TaintEffect = "NoExecute"
)
type TolerationOperator string
const (
TolerationOpExists TolerationOperator = "Exists"
TolerationOpEqual TolerationOperator = "Equal"
)
type SecurityContext struct {
Privileged *bool `mapstructure:"privileged"`
RunAsNonRoot *bool `mapstructure:"runAsNonRoot"`
RunAsUser *int64 `mapstructure:"runAsUser"`
RunAsGroup *int64 `mapstructure:"runAsGroup"`
FSGroup *int64 `mapstructure:"fsGroup"`
SeccompProfile *SecProfile `mapstructure:"seccompProfile"`
ApparmorProfile *SecProfile `mapstructure:"apparmorProfile"`
}
type SecProfile struct {
Type SecProfileType `mapstructure:"type"`
LocalhostProfile string `mapstructure:"localhostProfile"`
}
type SecProfileType string
const (
SecProfileTypeRuntimeDefault SecProfileType = "RuntimeDefault"
SecProfileTypeLocalhost SecProfileType = "Localhost"
)
func parseBackendOptions(step *backend.Step) (BackendOptions, error) {
var result BackendOptions
if step.BackendOptions == nil {
return result, nil
}
err := mapstructure.Decode(step.BackendOptions[EngineName], &result)
return result, err
}

View File

@ -0,0 +1,73 @@
package kubernetes
import (
"testing"
"github.com/stretchr/testify/assert"
backend "go.woodpecker-ci.org/woodpecker/v2/pipeline/backend/types"
)
func Test_parseBackendOptions(t *testing.T) {
got, err := parseBackendOptions(&backend.Step{BackendOptions: nil})
assert.NoError(t, err)
assert.Equal(t, BackendOptions{}, got)
got, err = parseBackendOptions(&backend.Step{BackendOptions: map[string]any{}})
assert.NoError(t, err)
assert.Equal(t, BackendOptions{}, got)
got, err = parseBackendOptions(&backend.Step{
BackendOptions: map[string]any{
"kubernetes": map[string]any{
"nodeSelector": map[string]string{"storage": "ssd"},
"serviceAccountName": "wp-svc-acc",
"tolerations": []map[string]any{
{"key": "net-port", "value": "100Mbit", "effect": TaintEffectNoSchedule},
},
"resources": map[string]any{
"requests": map[string]string{"memory": "128Mi", "cpu": "1000m"},
"limits": map[string]string{"memory": "256Mi", "cpu": "2"},
},
"securityContext": map[string]any{
"privileged": newBool(true),
"runAsNonRoot": newBool(true),
"runAsUser": newInt64(101),
"runAsGroup": newInt64(101),
"fsGroup": newInt64(101),
"seccompProfile": map[string]any{
"type": "Localhost",
"localhostProfile": "profiles/audit.json",
},
"apparmorProfile": map[string]any{
"type": "Localhost",
"localhostProfile": "k8s-apparmor-example-deny-write",
},
},
},
},
})
assert.NoError(t, err)
assert.Equal(t, BackendOptions{
NodeSelector: map[string]string{"storage": "ssd"},
ServiceAccountName: "wp-svc-acc",
Tolerations: []Toleration{{Key: "net-port", Value: "100Mbit", Effect: TaintEffectNoSchedule}},
Resources: Resources{
Requests: map[string]string{"memory": "128Mi", "cpu": "1000m"},
Limits: map[string]string{"memory": "256Mi", "cpu": "2"},
},
SecurityContext: &SecurityContext{
Privileged: newBool(true),
RunAsNonRoot: newBool(true),
RunAsUser: newInt64(101),
RunAsGroup: newInt64(101),
FSGroup: newInt64(101),
SeccompProfile: &SecProfile{
Type: "Localhost",
LocalhostProfile: "profiles/audit.json",
},
ApparmorProfile: &SecProfile{
Type: "Localhost",
LocalhostProfile: "k8s-apparmor-example-deny-write",
},
},
}, got)
}

View File

@ -172,7 +172,7 @@ func (e *kube) getConfig() *config {
return &c
}
// Setup the pipeline environment.
// SetupWorkflow sets up the pipeline environment.
func (e *kube) SetupWorkflow(ctx context.Context, conf *types.Config, taskUUID string) error {
log.Trace().Str("taskUUID", taskUUID).Msgf("Setting up Kubernetes primitives")
@ -183,7 +183,7 @@ func (e *kube) SetupWorkflow(ctx context.Context, conf *types.Config, taskUUID s
}
}
extraHosts := []types.HostAlias{}
var extraHosts []types.HostAlias
for _, stage := range conf.Stages {
for _, step := range stage.Steps {
if step.Type == types.StepTypeService {
@ -206,14 +206,19 @@ func (e *kube) SetupWorkflow(ctx context.Context, conf *types.Config, taskUUID s
return nil
}
// Start the pipeline step.
// StartStep starts the pipeline step.
func (e *kube) StartStep(ctx context.Context, step *types.Step, taskUUID string) error {
options, err := parseBackendOptions(step)
if err != nil {
log.Error().Err(err).Msg("could not parse backend options")
}
log.Trace().Str("taskUUID", taskUUID).Msgf("starting step: %s", step.Name)
_, err := startPod(ctx, e, step)
_, err = startPod(ctx, e, step, options)
return err
}
// Wait for the pipeline step to complete and returns
// WaitStep waits for the pipeline step to complete and returns
// the completion results.
func (e *kube) WaitStep(ctx context.Context, step *types.Step, taskUUID string) (*types.State, error) {
podName, err := stepToPodName(step)
@ -291,7 +296,7 @@ func (e *kube) WaitStep(ctx context.Context, step *types.Step, taskUUID string)
return bs, nil
}
// Tail the pipeline step logs.
// TailStep tails the pipeline step logs.
func (e *kube) TailStep(ctx context.Context, step *types.Step, taskUUID string) (io.ReadCloser, error) {
podName, err := stepToPodName(step)
if err != nil {
@ -369,7 +374,7 @@ func (e *kube) DestroyStep(ctx context.Context, step *types.Step, taskUUID strin
return err
}
// Destroy the pipeline environment.
// DestroyWorkflow destroys the pipeline environment.
func (e *kube) DestroyWorkflow(ctx context.Context, conf *types.Config, taskUUID string) error {
log.Trace().Str("taskUUID", taskUUID).Msg("deleting Kubernetes primitives")

View File

@ -35,20 +35,20 @@ const (
podPrefix = "wp-"
)
func mkPod(step *types.Step, config *config, podName, goos string) (*v1.Pod, error) {
func mkPod(step *types.Step, config *config, podName, goos string, options BackendOptions) (*v1.Pod, error) {
var err error
meta, err := podMeta(step, config, podName)
meta, err := podMeta(step, config, options, podName)
if err != nil {
return nil, err
}
spec, err := podSpec(step, config)
spec, err := podSpec(step, config, options)
if err != nil {
return nil, err
}
container, err := podContainer(step, podName, goos)
container, err := podContainer(step, podName, goos, options)
if err != nil {
return nil, err
}
@ -73,7 +73,7 @@ func podName(step *types.Step) (string, error) {
return dnsName(podPrefix + step.UUID)
}
func podMeta(step *types.Step, config *config, podName string) (metav1.ObjectMeta, error) {
func podMeta(step *types.Step, config *config, options BackendOptions, podName string) (metav1.ObjectMeta, error) {
var err error
meta := metav1.ObjectMeta{
Name: podName,
@ -98,7 +98,7 @@ func podMeta(step *types.Step, config *config, podName string) (metav1.ObjectMet
meta.Annotations = make(map[string]string)
}
securityContext := step.BackendOptions.Kubernetes.SecurityContext
securityContext := options.SecurityContext
if securityContext != nil {
key, value := apparmorAnnotation(podName, securityContext.ApparmorProfile)
if key != nil && value != nil {
@ -113,16 +113,16 @@ func stepLabel(step *types.Step) (string, error) {
return toDNSName(step.Name)
}
func podSpec(step *types.Step, config *config) (v1.PodSpec, error) {
func podSpec(step *types.Step, config *config, options BackendOptions) (v1.PodSpec, error) {
var err error
spec := v1.PodSpec{
RestartPolicy: v1.RestartPolicyNever,
ServiceAccountName: step.BackendOptions.Kubernetes.ServiceAccountName,
ServiceAccountName: options.ServiceAccountName,
ImagePullSecrets: imagePullSecretsReferences(config.ImagePullSecretNames),
HostAliases: hostAliases(step.ExtraHosts),
NodeSelector: nodeSelector(step.BackendOptions.Kubernetes.NodeSelector, step.Environment["CI_SYSTEM_PLATFORM"]),
Tolerations: tolerations(step.BackendOptions.Kubernetes.Tolerations),
SecurityContext: podSecurityContext(step.BackendOptions.Kubernetes.SecurityContext, config.SecurityContext),
NodeSelector: nodeSelector(options.NodeSelector, step.Environment["CI_SYSTEM_PLATFORM"]),
Tolerations: tolerations(options.Tolerations),
SecurityContext: podSecurityContext(options.SecurityContext, config.SecurityContext),
}
spec.Volumes, err = volumes(step.Volumes)
if err != nil {
@ -132,7 +132,7 @@ func podSpec(step *types.Step, config *config) (v1.PodSpec, error) {
return spec, nil
}
func podContainer(step *types.Step, podName, goos string) (v1.Container, error) {
func podContainer(step *types.Step, podName, goos string, options BackendOptions) (v1.Container, error) {
var err error
container := v1.Container{
Name: podName,
@ -156,9 +156,9 @@ func podContainer(step *types.Step, podName, goos string) (v1.Container, error)
container.Env = mapToEnvVars(step.Environment)
container.Ports = containerPorts(step.Ports)
container.SecurityContext = containerSecurityContext(step.BackendOptions.Kubernetes.SecurityContext, step.Privileged)
container.SecurityContext = containerSecurityContext(options.SecurityContext, step.Privileged)
container.Resources, err = resourceRequirements(step.BackendOptions.Kubernetes.Resources)
container.Resources, err = resourceRequirements(options.Resources)
if err != nil {
return container, err
}
@ -268,7 +268,7 @@ func imagePullSecretsReference(imagePullSecretName string) v1.LocalObjectReferen
}
}
func resourceRequirements(resources types.Resources) (v1.ResourceRequirements, error) {
func resourceRequirements(resources Resources) (v1.ResourceRequirements, error) {
var err error
requirements := v1.ResourceRequirements{}
@ -315,7 +315,7 @@ func nodeSelector(backendNodeSelector map[string]string, platform string) map[st
return nodeSelector
}
func tolerations(backendTolerations []types.Toleration) []v1.Toleration {
func tolerations(backendTolerations []Toleration) []v1.Toleration {
var tolerations []v1.Toleration
if len(backendTolerations) > 0 {
@ -329,7 +329,7 @@ func tolerations(backendTolerations []types.Toleration) []v1.Toleration {
return tolerations
}
func toleration(backendToleration types.Toleration) v1.Toleration {
func toleration(backendToleration Toleration) v1.Toleration {
return v1.Toleration{
Key: backendToleration.Key,
Operator: v1.TolerationOperator(backendToleration.Operator),
@ -339,7 +339,7 @@ func toleration(backendToleration types.Toleration) v1.Toleration {
}
}
func podSecurityContext(sc *types.SecurityContext, secCtxConf SecurityContextConfig) *v1.PodSecurityContext {
func podSecurityContext(sc *SecurityContext, secCtxConf SecurityContextConfig) *v1.PodSecurityContext {
var (
nonRoot *bool
user *int64
@ -381,7 +381,7 @@ func podSecurityContext(sc *types.SecurityContext, secCtxConf SecurityContextCon
return securityContext
}
func seccompProfile(scp *types.SecProfile) *v1.SeccompProfile {
func seccompProfile(scp *SecProfile) *v1.SeccompProfile {
if scp == nil || len(scp.Type) == 0 {
return nil
}
@ -397,7 +397,7 @@ func seccompProfile(scp *types.SecProfile) *v1.SeccompProfile {
return seccompProfile
}
func containerSecurityContext(sc *types.SecurityContext, stepPrivileged bool) *v1.SecurityContext {
func containerSecurityContext(sc *SecurityContext, stepPrivileged bool) *v1.SecurityContext {
var privileged *bool
if sc != nil && sc.Privileged != nil && *sc.Privileged {
@ -417,7 +417,7 @@ func containerSecurityContext(sc *types.SecurityContext, stepPrivileged bool) *v
return securityContext
}
func apparmorAnnotation(containerName string, scp *types.SecProfile) (*string, *string) {
func apparmorAnnotation(containerName string, scp *SecProfile) (*string, *string) {
if scp == nil {
return nil, nil
}
@ -428,12 +428,12 @@ func apparmorAnnotation(containerName string, scp *types.SecProfile) (*string, *
profilePath string
)
if scp.Type == types.SecProfileTypeRuntimeDefault {
if scp.Type == SecProfileTypeRuntimeDefault {
profileType = "runtime"
profilePath = "default"
}
if scp.Type == types.SecProfileTypeLocalhost {
if scp.Type == SecProfileTypeLocalhost {
profileType = "localhost"
profilePath = scp.LocalhostProfile
}
@ -458,13 +458,13 @@ func mapToEnvVars(m map[string]string) []v1.EnvVar {
return ev
}
func startPod(ctx context.Context, engine *kube, step *types.Step) (*v1.Pod, error) {
func startPod(ctx context.Context, engine *kube, step *types.Step, options BackendOptions) (*v1.Pod, error) {
podName, err := stepToPodName(step)
if err != nil {
return nil, err
}
engineConfig := engine.getConfig()
pod, err := mkPod(step, engineConfig, podName, engine.goos)
pod, err := mkPod(step, engineConfig, podName, engine.goos, options)
if err != nil {
return nil, err
}

View File

@ -139,7 +139,7 @@ func TestTinyPod(t *testing.T) {
Environment: map[string]string{"CI": "woodpecker"},
}, &config{
Namespace: "woodpecker",
}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64")
}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{})
assert.NoError(t, err)
podJSON, err := json.Marshal(pod)
@ -297,17 +297,17 @@ func TestFullPod(t *testing.T) {
{Number: 2345, Protocol: "tcp"},
{Number: 3456, Protocol: "udp"},
}
secCtx := types.SecurityContext{
secCtx := SecurityContext{
Privileged: newBool(true),
RunAsNonRoot: newBool(true),
RunAsUser: newInt64(101),
RunAsGroup: newInt64(101),
FSGroup: newInt64(101),
SeccompProfile: &types.SecProfile{
SeccompProfile: &SecProfile{
Type: "Localhost",
LocalhostProfile: "profiles/audit.json",
},
ApparmorProfile: &types.SecProfile{
ApparmorProfile: &SecProfile{
Type: "Localhost",
LocalhostProfile: "k8s-apparmor-example-deny-write",
},
@ -324,25 +324,22 @@ func TestFullPod(t *testing.T) {
Environment: map[string]string{"CGO": "0"},
ExtraHosts: hostAliases,
Ports: ports,
BackendOptions: types.BackendOptions{
Kubernetes: types.KubernetesBackendOptions{
NodeSelector: map[string]string{"storage": "ssd"},
ServiceAccountName: "wp-svc-acc",
Tolerations: []types.Toleration{{Key: "net-port", Value: "100Mbit", Effect: types.TaintEffectNoSchedule}},
Resources: types.Resources{
Requests: map[string]string{"memory": "128Mi", "cpu": "1000m"},
Limits: map[string]string{"memory": "256Mi", "cpu": "2"},
},
SecurityContext: &secCtx,
},
},
}, &config{
Namespace: "woodpecker",
ImagePullSecretNames: []string{"regcred", "another-pull-secret"},
PodLabels: map[string]string{"app": "test"},
PodAnnotations: map[string]string{"apps.kubernetes.io/pod-index": "0"},
SecurityContext: SecurityContextConfig{RunAsNonRoot: false},
}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64")
}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{
NodeSelector: map[string]string{"storage": "ssd"},
ServiceAccountName: "wp-svc-acc",
Tolerations: []Toleration{{Key: "net-port", Value: "100Mbit", Effect: TaintEffectNoSchedule}},
Resources: Resources{
Requests: map[string]string{"memory": "128Mi", "cpu": "1000m"},
Limits: map[string]string{"memory": "256Mi", "cpu": "2"},
},
SecurityContext: &secCtx,
})
assert.NoError(t, err)
podJSON, err := json.Marshal(pod)

View File

@ -60,8 +60,3 @@ type Backend interface {
type BackendInfo struct {
Platform string
}
// BackendOptions defines advanced options for specific backends
type BackendOptions struct {
Kubernetes KubernetesBackendOptions `json:"kubernetes,omitempty"`
}

View File

@ -1,76 +0,0 @@
// Copyright 2023 Woodpecker Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
// KubernetesBackendOptions defines all the advanced options for the kubernetes backend
type KubernetesBackendOptions struct {
Resources Resources `json:"resouces,omitempty"`
ServiceAccountName string `json:"serviceAccountName,omitempty"`
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
Tolerations []Toleration `json:"tolerations,omitempty"`
SecurityContext *SecurityContext `json:"securityContext,omitempty"`
}
// Resources defines two maps for kubernetes resource definitions
type Resources struct {
Requests map[string]string `json:"requests,omitempty"`
Limits map[string]string `json:"limits,omitempty"`
}
// Defines Kubernetes toleration
type Toleration struct {
Key string `json:"key,omitempty"`
Operator TolerationOperator `json:"operator,omitempty"`
Value string `json:"value,omitempty"`
Effect TaintEffect `json:"effect,omitempty"`
TolerationSeconds *int64 `json:"tolerationSeconds,omitempty"`
}
type TaintEffect string
const (
TaintEffectNoSchedule TaintEffect = "NoSchedule"
TaintEffectPreferNoSchedule TaintEffect = "PreferNoSchedule"
TaintEffectNoExecute TaintEffect = "NoExecute"
)
type TolerationOperator string
const (
TolerationOpExists TolerationOperator = "Exists"
TolerationOpEqual TolerationOperator = "Equal"
)
type SecurityContext struct {
Privileged *bool `json:"privileged,omitempty"`
RunAsNonRoot *bool `json:"runAsNonRoot,omitempty"`
RunAsUser *int64 `json:"runAsUser,omitempty"`
RunAsGroup *int64 `json:"runAsGroup,omitempty"`
FSGroup *int64 `json:"fsGroup,omitempty"`
SeccompProfile *SecProfile `json:"seccompProfile,omitempty"`
ApparmorProfile *SecProfile `json:"apparmorProfile,omitempty"`
}
type SecProfile struct {
Type SecProfileType `json:"type,omitempty"`
LocalhostProfile string `json:"localhostProfile,omitempty"`
}
type SecProfileType string
const (
SecProfileTypeRuntimeDefault SecProfileType = "RuntimeDefault"
SecProfileTypeLocalhost SecProfileType = "Localhost"
)

View File

@ -46,7 +46,7 @@ type Step struct {
AuthConfig Auth `json:"auth_config,omitempty"`
NetworkMode string `json:"network_mode,omitempty"`
Ports []Port `json:"ports,omitempty"`
BackendOptions BackendOptions `json:"backend_options,omitempty"`
BackendOptions map[string]any `json:"backend_options,omitempty"`
}
// StepType identifies the type of step

View File

@ -34,7 +34,6 @@ type Registry struct {
Hostname string
Username string
Password string
Email string
Token string
}

View File

@ -134,11 +134,6 @@ func (c *Compiler) createProcess(container *yaml_types.Container, stepType backe
}
}
// Advanced backend settings
backendOptions := backend_types.BackendOptions{
Kubernetes: convertKubernetesBackendOptions(&container.BackendOptions.Kubernetes),
}
memSwapLimit := int64(container.MemSwapLimit)
if c.reslimit.MemSwapLimit != 0 {
memSwapLimit = c.reslimit.MemSwapLimit
@ -214,7 +209,7 @@ func (c *Compiler) createProcess(container *yaml_types.Container, stepType backe
Failure: failure,
NetworkMode: networkMode,
Ports: ports,
BackendOptions: backendOptions,
BackendOptions: container.BackendOptions,
}, nil
}
@ -240,52 +235,3 @@ func convertPort(portDef string) (backend_types.Port, error) {
return port, nil
}
func convertKubernetesBackendOptions(kubeOpt *yaml_types.KubernetesBackendOptions) backend_types.KubernetesBackendOptions {
resources := backend_types.Resources{
Limits: kubeOpt.Resources.Limits,
Requests: kubeOpt.Resources.Requests,
}
var tolerations []backend_types.Toleration
for _, t := range kubeOpt.Tolerations {
tolerations = append(tolerations, backend_types.Toleration{
Key: t.Key,
Operator: backend_types.TolerationOperator(t.Operator),
Value: t.Value,
Effect: backend_types.TaintEffect(t.Effect),
TolerationSeconds: t.TolerationSeconds,
})
}
var securityContext *backend_types.SecurityContext
if kubeOpt.SecurityContext != nil {
securityContext = &backend_types.SecurityContext{
Privileged: kubeOpt.SecurityContext.Privileged,
RunAsNonRoot: kubeOpt.SecurityContext.RunAsNonRoot,
RunAsUser: kubeOpt.SecurityContext.RunAsUser,
RunAsGroup: kubeOpt.SecurityContext.RunAsGroup,
FSGroup: kubeOpt.SecurityContext.FSGroup,
}
if kubeOpt.SecurityContext.SeccompProfile != nil {
securityContext.SeccompProfile = &backend_types.SecProfile{
Type: backend_types.SecProfileType(kubeOpt.SecurityContext.SeccompProfile.Type),
LocalhostProfile: kubeOpt.SecurityContext.SeccompProfile.LocalhostProfile,
}
}
if kubeOpt.SecurityContext.ApparmorProfile != nil {
securityContext.ApparmorProfile = &backend_types.SecProfile{
Type: backend_types.SecProfileType(kubeOpt.SecurityContext.ApparmorProfile.Type),
LocalhostProfile: kubeOpt.SecurityContext.ApparmorProfile.LocalhostProfile,
}
}
}
return backend_types.KubernetesBackendOptions{
Resources: resources,
ServiceAccountName: kubeOpt.ServiceAccountName,
NodeSelector: kubeOpt.NodeSelector,
Tolerations: tolerations,
SecurityContext: securityContext,
}
}

View File

@ -0,0 +1,10 @@
steps:
build:
image: golang
commands:
- go build
- go test
backend_options:
custom_backend:
option1: xyz
option2: [1, 2, 3]

View File

@ -111,6 +111,11 @@ func TestSchema(t *testing.T) {
testFile: ".woodpecker/test-dag.yaml",
fail: false,
},
{
name: "Custom backend",
testFile: ".woodpecker/test-custom-backend.yaml",
fail: false,
},
}
for _, tt := range testTable {

View File

@ -1,71 +0,0 @@
// Copyright 2023 Woodpecker Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
// BackendOptions are advanced options for specific backends
type BackendOptions struct {
Kubernetes KubernetesBackendOptions `yaml:"kubernetes,omitempty"`
}
type KubernetesBackendOptions struct {
Resources Resources `yaml:"resources,omitempty"`
ServiceAccountName string `yaml:"serviceAccountName,omitempty"`
NodeSelector map[string]string `yaml:"nodeSelector,omitempty"`
Tolerations []Toleration `yaml:"tolerations,omitempty"`
SecurityContext *SecurityContext `yaml:"securityContext,omitempty"`
}
type Resources struct {
Requests map[string]string `yaml:"requests,omitempty"`
Limits map[string]string `yaml:"limits,omitempty"`
}
type Toleration struct {
Key string `yaml:"key,omitempty"`
Operator TolerationOperator `yaml:"operator,omitempty"`
Value string `yaml:"value,omitempty"`
Effect TaintEffect `yaml:"effect,omitempty"`
TolerationSeconds *int64 `yaml:"tolerationSeconds,omitempty"`
}
type TaintEffect string
const (
TaintEffectNoSchedule TaintEffect = "NoSchedule"
TaintEffectPreferNoSchedule TaintEffect = "PreferNoSchedule"
TaintEffectNoExecute TaintEffect = "NoExecute"
)
type TolerationOperator string
const (
TolerationOpExists TolerationOperator = "Exists"
TolerationOpEqual TolerationOperator = "Equal"
)
type SecurityContext struct {
Privileged *bool `yaml:"privileged,omitempty"`
RunAsNonRoot *bool `yaml:"runAsNonRoot,omitempty"`
RunAsUser *int64 `yaml:"runAsUser,omitempty"`
RunAsGroup *int64 `yaml:"runAsGroup,omitempty"`
FSGroup *int64 `yaml:"fsGroup,omitempty"`
SeccompProfile *SecProfile `yaml:"seccompProfile,omitempty"`
ApparmorProfile *SecProfile `yaml:"apparmorProfile,omitempty"`
}
type SecProfile struct {
Type string `yaml:"type,omitempty"`
LocalhostProfile string `yaml:"localhostProfile,omitempty"`
}

View File

@ -33,7 +33,7 @@ type (
// Container defines a container.
Container struct {
BackendOptions BackendOptions `yaml:"backend_options,omitempty"`
BackendOptions map[string]any `yaml:"backend_options,omitempty"`
Commands base.StringOrSlice `yaml:"commands,omitempty"`
Entrypoint base.StringOrSlice `yaml:"entrypoint,omitempty"`
Detached bool `yaml:"detach,omitempty"`