Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for default pod template #1901

Merged
merged 1 commit into from
Jan 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/config-defaults.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,8 @@ data:
# TaskRuns. If a user's requested TaskRun specifies another value for this
# label, the user's request supercedes.
default-managed-by-label-value: "tekton-pipelines"

# default-pod-template contains the default pod template to use
# TaskRun and PipelineRun, if none is specified. If a pod template
# is specified, the default pod template is ignored.
# default-pod-template:
28 changes: 18 additions & 10 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ stringData:
https_validate_certificates = True
---
apiVersion: v1
data: null
kind: ConfigMap
metadata:
name: config-artifact-pvc
Expand All @@ -181,24 +180,33 @@ creation of a persistent volume could be slower than uploading/downloading files
to a bucket, or if the the cluster is running in multiple zones, the access to
the persistent volume can fail.

### Overriding default ServiceAccount used for TaskRun and PipelineRun
### Overriding default ServiceAccount, Timeout or PodTemplate used for TaskRun and PipelineRun

The ConfigMap `config-defaults` can be used to override default service account
e.g. to override the default service account (`default`) to `tekton` apply the
following
The ConfigMap `config-defaults` can be used to override default values.
You can override the default service account, the default timeout, and the
default pod template applied to `TaskRun` and `PipelineRun`.

```yaml
The example below overrides the following :
- the default service account (`default`) to `tekton`
- the default timeout (60 minutes) to 20 minutes
- the default pod template to include an annotation preventing clusterautoscaler to evict a running task pod
(see [here](./taskruns.md#pod-template) or [here](./pipelineruns.md#pod-template) for more infos on pod templates)

### config-defaults.yaml
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: config-defaults
data:
default-service-account: "tekton"

default-timeout-minutes: "20"
default-pod-template: |
annotations:
cluster-autoscaler.kubernetes.io/safe-to-evict: 'false'
```

*NOTE:* The `_example` key contains of the keys that can be overriden and their
default values.
*NOTE:* The `_example` key in the provided [config-defaults.yaml](./../config/config-defaults.yaml)
file contains the keys that can be overriden and their default values.

## Custom Releases

Expand Down
4 changes: 4 additions & 0 deletions docs/podtemplates.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ configuration that will be used as the basis for the `Task` pod.

This allows to customize some Pod specific field per `Task` execution, aka `TaskRun`.

Alternatively, you can also define a default pod template in tekton config, see [here](./install.md)
When a pod template is specified for a `PipelineRun` or `TaskRun`, the default pod template is ignored, ie
both templates are **NOT** merged, it's always one or the other.

---

The current fields supported are:
Expand Down
5 changes: 5 additions & 0 deletions hack/update-codegen.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ ${GOPATH}/bin/deepcopy-gen \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt \
-i github.com/tektoncd/pipeline/pkg/apis/config

${GOPATH}/bin/deepcopy-gen \
-O zz_generated.deepcopy \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt \
-i github.com/tektoncd/pipeline/pkg/apis/pipeline/pod

# Knative Injection
# This generates the knative injection packages for the resource package (v1alpha1).
# This is separate from the pipeline package for the same reason as client and all (see above).
Expand Down
25 changes: 24 additions & 1 deletion pkg/apis/config/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"strconv"
"time"

"github.com/ghodss/yaml"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/pod"
corev1 "k8s.io/api/core/v1"
)

Expand All @@ -33,6 +35,7 @@ const (
defaultServiceAccountKey = "default-service-account"
defaultManagedByLabelValueKey = "default-managed-by-label-value"
DefaultManagedByLabelValue = "tekton-pipelines"
defaultPodTemplateKey = "default-pod-template"
)

// Defaults holds the default configurations
Expand All @@ -41,13 +44,23 @@ type Defaults struct {
DefaultTimeoutMinutes int
DefaultServiceAccount string
DefaultManagedByLabelValue string
DefaultPodTemplate *pod.Template
}

// Equals returns true if two Configs are identical
func (cfg *Defaults) Equals(other *Defaults) bool {
if cfg == nil && other == nil {
return true
}

if cfg == nil || other == nil {
return false
}

return other.DefaultTimeoutMinutes == cfg.DefaultTimeoutMinutes &&
other.DefaultServiceAccount == cfg.DefaultServiceAccount &&
other.DefaultManagedByLabelValue == cfg.DefaultManagedByLabelValue
other.DefaultManagedByLabelValue == cfg.DefaultManagedByLabelValue &&
other.DefaultPodTemplate.Equals(cfg.DefaultPodTemplate)
}

// NewDefaultsFromMap returns a Config given a map corresponding to a ConfigMap
Expand All @@ -56,6 +69,7 @@ func NewDefaultsFromMap(cfgMap map[string]string) (*Defaults, error) {
DefaultTimeoutMinutes: DefaultTimeoutMinutes,
DefaultManagedByLabelValue: DefaultManagedByLabelValue,
}

if defaultTimeoutMin, ok := cfgMap[defaultTimeoutMinutesKey]; ok {
timeout, err := strconv.ParseInt(defaultTimeoutMin, 10, 0)
if err != nil {
Expand All @@ -67,10 +81,19 @@ func NewDefaultsFromMap(cfgMap map[string]string) (*Defaults, error) {
if defaultServiceAccount, ok := cfgMap[defaultServiceAccountKey]; ok {
tc.DefaultServiceAccount = defaultServiceAccount
}

if defaultManagedByLabelValue, ok := cfgMap[defaultManagedByLabelValueKey]; ok {
tc.DefaultManagedByLabelValue = defaultManagedByLabelValue
}

if defaultPodTemplate, ok := cfgMap[defaultPodTemplateKey]; ok {
var podTemplate pod.Template
if err := yaml.Unmarshal([]byte(defaultPodTemplate), &podTemplate); err != nil {
return nil, fmt.Errorf("failed to unmarshal %v", defaultPodTemplate)
}
tc.DefaultPodTemplate = &podTemplate
}

return &tc, nil
}

Expand Down
158 changes: 153 additions & 5 deletions pkg/apis/config/default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,58 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/pod"
test "github.com/tektoncd/pipeline/pkg/reconciler/testing"
)

func TestNewDefaultsFromConfigMap(t *testing.T) {
expectedConfig := &Defaults{
DefaultTimeoutMinutes: 50,
DefaultServiceAccount: "tekton",
DefaultManagedByLabelValue: "something-else",
type testCase struct {
expectedConfig *Defaults
expectedError bool
fileName string
}

testCases := []testCase{
{
expectedConfig: &Defaults{
DefaultTimeoutMinutes: 50,
DefaultServiceAccount: "tekton",
DefaultManagedByLabelValue: "something-else",
},
fileName: DefaultsConfigName,
},
{
expectedConfig: &Defaults{
DefaultTimeoutMinutes: 50,
DefaultServiceAccount: "tekton",
DefaultManagedByLabelValue: DefaultManagedByLabelValue,
DefaultPodTemplate: &pod.Template{
NodeSelector: map[string]string{
"label": "value",
},
},
},
fileName: "config-defaults-with-pod-template",
},
// the github.com/ghodss/yaml package in the vendor directory does not support UnmarshalStrict
// update it, switch to UnmarshalStrict in defaults.go, then uncomment these tests
// {
// expectedError: true,
// fileName: "config-defaults-timeout-err",
// },
// {
// expectedError: true,
// fileName: "config-defaults-pod-template-err",
// },
}

for _, tc := range testCases {
if tc.expectedError {
verifyConfigFileWithExpectedError(t, tc.fileName)
} else {
verifyConfigFileWithExpectedConfig(t, tc.fileName, tc.expectedConfig)
}
}
verifyConfigFileWithExpectedConfig(t, DefaultsConfigName, expectedConfig)
}

func TestNewDefaultsFromEmptyConfigMap(t *testing.T) {
Expand All @@ -41,6 +83,105 @@ func TestNewDefaultsFromEmptyConfigMap(t *testing.T) {
verifyConfigFileWithExpectedConfig(t, DefaultsConfigEmptyName, expectedConfig)
}

func TestEquals(t *testing.T) {
testCases := []struct {
name string
left *Defaults
right *Defaults
expected bool
}{
{
name: "left and right nil",
left: nil,
right: nil,
expected: true,
},
{
name: "left nil",
left: nil,
right: &Defaults{},
expected: false,
},
{
name: "right nil",
left: &Defaults{},
right: nil,
expected: false,
},
{
name: "right and right default",
left: &Defaults{},
right: &Defaults{},
expected: true,
},
{
name: "different default timeout",
left: &Defaults{
DefaultTimeoutMinutes: 10,
},
right: &Defaults{
DefaultTimeoutMinutes: 20,
},
expected: false,
},
{
name: "same default timeout",
left: &Defaults{
DefaultTimeoutMinutes: 20,
},
right: &Defaults{
DefaultTimeoutMinutes: 20,
},
expected: true,
},
{
name: "different default pod template",
left: &Defaults{
DefaultPodTemplate: &pod.Template{
NodeSelector: map[string]string{
"label": "value",
},
},
},
right: &Defaults{
DefaultPodTemplate: &pod.Template{
NodeSelector: map[string]string{
"label2": "value",
},
},
},
expected: false,
},
{
name: "same default pod template",
left: &Defaults{
DefaultPodTemplate: &pod.Template{
NodeSelector: map[string]string{
"label": "value",
},
},
},
right: &Defaults{
DefaultPodTemplate: &pod.Template{
NodeSelector: map[string]string{
"label": "value",
},
},
},
expected: true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actual := tc.left.Equals(tc.right)
if actual != tc.expected {
t.Errorf("Comparison failed expected: %t, actual: %t", tc.expected, actual)
}
})
}
}

func verifyConfigFileWithExpectedConfig(t *testing.T, fileName string, expectedConfig *Defaults) {
cm := test.ConfigMapFromTestFile(t, fileName)
if Defaults, err := NewDefaultsFromConfigMap(cm); err == nil {
Expand All @@ -51,3 +192,10 @@ func verifyConfigFileWithExpectedConfig(t *testing.T, fileName string, expectedC
t.Errorf("NewDefaultsFromConfigMap(actual) = %v", err)
}
}

func verifyConfigFileWithExpectedError(t *testing.T, fileName string) {
cm := test.ConfigMapFromTestFile(t, fileName)
if _, err := NewDefaultsFromConfigMap(cm); err == nil {
t.Errorf("NewDefaultsFromConfigMap(actual) was expected to return an error")
}
}
24 changes: 24 additions & 0 deletions pkg/apis/config/testdata/config-defaults-pod-template-err.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2019 The Tekton 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
#
# https://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.

apiVersion: v1
kind: ConfigMap
metadata:
name: config-defaults
namespace: tekton-pipelines
data:
default-timeout-minutes: "50"
default-service-account: "tekton"
default-pod-template: |
badKey: 42
22 changes: 22 additions & 0 deletions pkg/apis/config/testdata/config-defaults-timeout-err.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2019 The Tekton 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
#
# https://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.

apiVersion: v1
kind: ConfigMap
metadata:
name: config-defaults
namespace: tekton-pipelines
data:
default-timeout-minutes: "abc"
default-service-account: "tekton"
Loading