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

Plumb through configurable defaulting in the webhook. #3546

Merged
merged 1 commit into from
Mar 27, 2019
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
4 changes: 2 additions & 2 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ required = [

[[override]]
name = "github.com/knative/pkg"
# HEAD as of 2019-03-25
revision = "04154dda9a1b8ef17939d30a628272b201330ddb"
# HEAD as of 2019-03-26
revision = "0f749ef7d5e65aba4e8a76aa34b4744685981a91"

[[override]]
name = "go.uber.org/zap"
Expand Down
11 changes: 11 additions & 0 deletions cmd/webhook/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package main

import (
"context"
"flag"
"log"

Expand All @@ -31,6 +32,7 @@ import (
"github.com/knative/pkg/version"
"github.com/knative/pkg/webhook"
kpa "github.com/knative/serving/pkg/apis/autoscaling/v1alpha1"
apiconfig "github.com/knative/serving/pkg/apis/config"
net "github.com/knative/serving/pkg/apis/networking/v1alpha1"
"github.com/knative/serving/pkg/apis/serving/v1alpha1"
"github.com/knative/serving/pkg/logging"
Expand Down Expand Up @@ -83,6 +85,10 @@ func main() {
// Watch the logging config map and dynamically update logging levels.
configMapWatcher := configmap.NewInformedWatcher(kubeClient, system.Namespace())
configMapWatcher.Watch(logging.ConfigMapName(), logging.UpdateLevelFromConfigMap(logger, atomicLevel, component))

store := apiconfig.NewStore(logger.Named("config-store"))
store.WatchConfigs(configMapWatcher)

if err = configMapWatcher.Start(stopCh); err != nil {
logger.Fatalw("Failed to start the ConfigMap watcher", zap.Error(err))
}
Expand Down Expand Up @@ -110,6 +116,11 @@ func main() {
},
Logger: logger,
DisallowUnknownFields: true,

// Decorate contexts with the current state of the config.
WithContext: func(ctx context.Context) context.Context {
return store.ToContext(ctx)
},
}
if err = controller.Run(stopCh); err != nil {
logger.Fatalw("Failed to start the admission controller", zap.Error(err))
Expand Down
46 changes: 46 additions & 0 deletions config/config-defaults.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Copyright 2019 The Knative 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: knative-serving
labels:
serving.knative.dev/release: devel

data:
_example: |
################################
# #
# EXAMPLE CONFIGURATION #
# #
################################

# This block is not actually functional configuration,
# but serves to illustrate the available configuration
# options and document them in a way that is accessible
# to users that `kubectl edit` this config map.
#
# These sample configuration options may be copied out of
# this block and unindented to actually change the configuration.

# revision-timeout-seconds contains the default number of
# seconds to use for the revision's per-request timeout, if
# none is specified.
revision-timeout-seconds: "300" # 5 minutes

# revision-cpu-limit contains the cpu allocation to assign
# to revisions by default.
revision-cpu-limit: "400m" # 0.4 of a CPU (aka 400 milli-CPU)
1 change: 1 addition & 0 deletions hack/update-codegen.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ ${CODEGEN_PKG}/generate-groups.sh "deepcopy,client,informer,lister" \
${GOPATH}/bin/deepcopy-gen \
-O zz_generated.deepcopy \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt \
-i github.com/knative/serving/pkg/apis/config \
-i github.com/knative/serving/pkg/reconciler/v1alpha1/clusteringress/config \
-i github.com/knative/serving/pkg/reconciler/v1alpha1/configuration/config \
-i github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config \
Expand Down
99 changes: 99 additions & 0 deletions pkg/apis/config/defaults.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
Copyright 2019 The Knative 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.
*/

package config

import (
"strconv"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
)

const (
// DefaultsConfigName is the name of config map for the defaults.
DefaultsConfigName = "config-defaults"
)

// NewDefaultsConfigFromMap creates a Defaults from the supplied Map
func NewDefaultsConfigFromMap(data map[string]string) (*Defaults, error) {
nc := &Defaults{}

// Process int64 fields
for _, i64 := range []struct {
key string
field *int64
// specified exactly when optional
defaultValue int64
}{{
key: "revision-timeout-seconds",
field: &nc.RevisionTimeoutSeconds,
defaultValue: DefaultRevisionTimeoutSeconds,
}} {
if raw, ok := data[i64.key]; !ok {
*i64.field = i64.defaultValue
} else if val, err := strconv.ParseInt(raw, 10, 64); err != nil {
return nil, err
} else {
*i64.field = val
}
}

// Process resource quantity fields
for _, rsrc := range []struct {
key string
field *resource.Quantity
// specified exactly when optional
defaultValue resource.Quantity
}{{
key: "revision-cpu-limit",
field: &nc.RevisionCPULimit,
defaultValue: DefaultRevisionCPULimit,
}} {
if raw, ok := data[rsrc.key]; !ok {
*rsrc.field = rsrc.defaultValue
} else if val, err := resource.ParseQuantity(raw); err != nil {
return nil, err
} else {
*rsrc.field = val
}
}

return nc, nil
}

// NewDefaultsConfigFromConfigMap creates a Defaults from the supplied configMap
func NewDefaultsConfigFromConfigMap(config *corev1.ConfigMap) (*Defaults, error) {
return NewDefaultsConfigFromMap(config.Data)
}

const (
// DefaultRevisionTimeoutSeconds will be set if timeoutSeconds not specified.
DefaultRevisionTimeoutSeconds = 5 * 60
)

// Pseudo-constants
var (
// DefaultRevisionCPULimit will be set if resources.limits.cpu is not specified.
DefaultRevisionCPULimit = resource.MustParse("400m")
)

// Defaults includes the default values to be populated by the webhook.
type Defaults struct {
RevisionTimeoutSeconds int64

RevisionCPULimit resource.Quantity
}
119 changes: 119 additions & 0 deletions pkg/apis/config/defaults_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
Copyright 2019 The Knative 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 config

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/knative/pkg/system"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

. "github.com/knative/serving/pkg/reconciler/testing"
)

func TestDefaultsConfigurationFromFile(t *testing.T) {
cm, example := ConfigMapsFromTestFile(t, DefaultsConfigName)

if _, err := NewDefaultsConfigFromConfigMap(cm); err != nil {
t.Errorf("NewDefaultsConfigFromConfigMap(actual) = %v", err)
}

if _, err := NewDefaultsConfigFromConfigMap(example); err != nil {
t.Errorf("NewDefaultsConfigFromConfigMap(example) = %v", err)
}
}

func TestDefaultsConfiguration(t *testing.T) {
configTests := []struct {
name string
wantErr bool
wantDefaults interface{}
config *corev1.ConfigMap
}{{
name: "defaults configuration",
wantErr: false,
wantDefaults: &Defaults{
RevisionTimeoutSeconds: DefaultRevisionTimeoutSeconds,
RevisionCPULimit: DefaultRevisionCPULimit,
},
config: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: system.Namespace(),
Name: DefaultsConfigName,
},
Data: map[string]string{},
},
}, {
name: "specified values",
wantErr: false,
wantDefaults: &Defaults{
RevisionTimeoutSeconds: 123,
RevisionCPULimit: resource.MustParse("123m"),
},
config: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: system.Namespace(),
Name: DefaultsConfigName,
},
Data: map[string]string{
"revision-timeout-seconds": "123",
"revision-cpu-limit": "123m",
},
},
}, {
name: "bad revision timeout",
wantErr: true,
wantDefaults: (*Defaults)(nil),
config: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: system.Namespace(),
Name: DefaultsConfigName,
},
Data: map[string]string{
"revision-timeout-seconds": "asdf",
},
},
}, {
name: "bad resource",
wantErr: true,
wantDefaults: (*Defaults)(nil),
config: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: system.Namespace(),
Name: DefaultsConfigName,
},
Data: map[string]string{
"revision-cpu-limit": "bad",
},
},
}}

for _, tt := range configTests {
actualDefaults, err := NewDefaultsConfigFromConfigMap(tt.config)

if (err != nil) != tt.wantErr {
t.Fatalf("Test: %q; NewDefaultsConfigFromConfigMap() error = %v, WantErr %v", tt.name, err, tt.wantErr)
}

if diff := cmp.Diff(actualDefaults, tt.wantDefaults, ignoreResourceQuantity); diff != "" {
t.Fatalf("Test: %q; want %v, but got %v", tt.name, tt.wantDefaults, actualDefaults)
}
}
}
21 changes: 21 additions & 0 deletions pkg/apis/config/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
Copyright 2019 The Knative 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.
*/

// +k8s:deepcopy-gen=package

// Package config holds the typed objects that define the schemas for
// ConfigMap objects that pertain to our API objects.
package config
Loading