Skip to content
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: 3 additions & 1 deletion cmd/ela-webhook/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ go_library(
importpath = "github.com/elafros/elafros/cmd/ela-webhook",
visibility = ["//visibility:private"],
deps = [
"//pkg/logging:go_default_library",
"//pkg/signals:go_default_library",
"//pkg/webhook:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/josephburnett/k8sflag/pkg/k8sflag:go_default_library",
"//vendor/go.uber.org/zap:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
],
Expand Down
20 changes: 13 additions & 7 deletions cmd/ela-webhook/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,36 @@ package main
import (
"flag"

"go.uber.org/zap"

"github.com/elafros/elafros/pkg/logging"
"github.com/elafros/elafros/pkg/signals"
"github.com/elafros/elafros/pkg/webhook"

"github.com/golang/glog"
"github.com/josephburnett/k8sflag/pkg/k8sflag"

"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)

func main() {
flag.Parse()
glog.Info("Starting the Configuration Webhook...")
loggingZapCfg := k8sflag.String("logging.zap-config", "")
logger := logging.NewLogger(loggingZapCfg.Get()).Named("ela-webhook")
defer logger.Sync()

logger.Info("Starting the Configuration Webhook")

// set up signals so we handle the first shutdown signal gracefully
stopCh := signals.SetupSignalHandler()

clusterConfig, err := rest.InClusterConfig()
if err != nil {
glog.Fatal(err.Error())
logger.Fatal("Failed to get in cluster config", zap.Error(err))
}

clientset, err := kubernetes.NewForConfig(clusterConfig)
if err != nil {
glog.Fatal(err)
logger.Fatal("Failed to get the client set", zap.Error(err))
}

options := webhook.ControllerOptions{
Expand All @@ -51,9 +57,9 @@ func main() {
SecretName: "ela-webhook-certs",
WebhookName: "webhook.elafros.dev",
}
controller, err := webhook.NewAdmissionController(clientset, options)
controller, err := webhook.NewAdmissionController(clientset, options, logger)
if err != nil {
glog.Fatal(err)
logger.Fatal("Failed to create the admission controller", zap.Error(err))
}
controller.Run(stopCh)
}
6 changes: 6 additions & 0 deletions config/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ k8s_object(
template = "elaconfig.yaml",
)

k8s_object(
name = "elawebhookconfig",
template = "elawebhookconfig.yaml",
)

k8s_object(
name = "controller",
images = {
Expand Down Expand Up @@ -110,6 +115,7 @@ k8s_objects(
":authz",
":crds",
":elaconfig",
":elawebhookconfig",
":controller",
":controllerservice",
":webhook",
Expand Down
2 changes: 0 additions & 2 deletions config/controller.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ spec:
# and substituted here.
image: github.com/elafros/elafros/cmd/ela-controller
args:
- "-logtostderr=true"
- "-stderrthreshold=INFO"
- "-queueSidecarImage"
# This is the Go import path for the binary that is containerized
# and substituted here.
Expand Down
46 changes: 46 additions & 0 deletions config/elawebhookconfig.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Copyright 2018 Google LLC
#
# 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: ela-webhook-config
namespace: ela-system
data:
# Logging configuration
logging.zap-config: |
{
"level": "info",
"development": false,
"sampling": {
"initial": 100,
"thereafter": 100
},
"outputPaths": ["stdout"],
"errorOutputPaths": ["stderr"],
"encoding": "json",
"encoderConfig": {
"timeKey": "",
"levelKey": "level",
"nameKey": "logger",
"callerKey": "caller",
"messageKey": "msg",
"stacktraceKey": "stacktrace",
"lineEnding": "",
"levelEncoder": "",
"timeEncoder": "",
"durationEncoder": "",
"callerEncoder": ""
}
}
10 changes: 7 additions & 3 deletions config/webhook.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ spec:
# This is the Go import path for the binary that is containerized
# and substituted here.
image: github.com/elafros/elafros/cmd/ela-webhook
args:
- "-logtostderr=true"
- "-stderrthreshold=INFO"
volumeMounts:
- name: ela-webhook-config
mountPath: /etc/config
volumes:
- name: ela-webhook-config
configMap:
name: ela-webhook-config
18 changes: 18 additions & 0 deletions pkg/logging/logkey/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,22 @@ const (

// JSONConfig is the key used for JSON configurations (not to be confused by the Configuration object)
JSONConfig = "elafros.dev/jsonconfig"

// Kind is the key used to represent kind of an object in logs
Kind = "elafros.dev/kind"

// Name is the key used to represent name of an object in logs
Name = "elafros.dev/name"

// Operation is the key used to represent an operation in logs
Operation = "elafros.dev/operation"

// Resource is the key used to represent a resource in logs
Resource = "elafros.dev/resource"

// SubResource is a generic key used to represent a sub-resource in logs
SubResource = "elafros.dev/subresource"

// UserInfo is the key used to represent a user information in logs
UserInfo = "elafros.dev/userinfo"
)
6 changes: 5 additions & 1 deletion pkg/webhook/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ go_library(
deps = [
"//pkg/apis/ela:go_default_library",
"//pkg/apis/ela/v1alpha1:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//pkg/logging:go_default_library",
"//pkg/logging/logkey:go_default_library",
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
"//vendor/github.com/mattbaird/jsonpatch:go_default_library",
"//vendor/go.uber.org/zap:go_default_library",
"//vendor/k8s.io/api/admission/v1beta1:go_default_library",
"//vendor/k8s.io/api/admissionregistration/v1beta1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
Expand All @@ -41,7 +43,9 @@ go_test(
embed = [":go_default_library"],
deps = [
"//pkg/apis/ela/v1alpha1:go_default_library",
"//pkg/logging:go_default_library",
"//vendor/github.com/mattbaird/jsonpatch:go_default_library",
"//vendor/go.uber.org/zap:go_default_library",
"//vendor/k8s.io/api/admission/v1beta1:go_default_library",
"//vendor/k8s.io/api/admissionregistration/v1beta1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
Expand Down
25 changes: 15 additions & 10 deletions pkg/webhook/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ limitations under the License.
package webhook

import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
Expand All @@ -25,7 +26,9 @@ import (
"math/big"
"time"

"github.com/golang/glog"
"go.uber.org/zap"

"github.com/elafros/elafros/pkg/logging"
)

const (
Expand Down Expand Up @@ -98,22 +101,23 @@ func createCert(template, parent *x509.Certificate, pub interface{}, parentPriv
return
}

func createCA() (*rsa.PrivateKey, *x509.Certificate, []byte, error) {
func createCA(ctx context.Context) (*rsa.PrivateKey, *x509.Certificate, []byte, error) {
logger := logging.FromContext(ctx)
rootKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
glog.Warningf("error generating random key: %s", err)
logger.Error("error generating random key", zap.Error(err))
return nil, nil, nil, err
}

rootCertTmpl, err := createCACertTemplate()
if err != nil {
glog.Warningf("error generating CA cert: %s", err)
logger.Error("error generating CA cert", zap.Error(err))
return nil, nil, nil, err
}

rootCert, rootCertPEM, err := createCert(rootCertTmpl, rootCertTmpl, &rootKey.PublicKey, rootKey)
if err != nil {
glog.Warningf("error signing the CA cert: %s", err)
logger.Error("error signing the CA cert", zap.Error(err))
return nil, nil, nil, err
}
return rootKey, rootCert, rootCertPEM, nil
Expand All @@ -123,29 +127,30 @@ func createCA() (*rsa.PrivateKey, *x509.Certificate, []byte, error) {
// key for the server. serverKey and serverCert are used by the server
// to establish trust for clients, CA certificate is used by the
// client to verify the server authentication chain.
func CreateCerts() (serverKey, serverCert, caCert []byte, err error) {
func CreateCerts(ctx context.Context) (serverKey, serverCert, caCert []byte, err error) {
logger := logging.FromContext(ctx)
// First create a CA certificate and private key
caKey, caCertificate, caCertificatePEM, err := createCA()
caKey, caCertificate, caCertificatePEM, err := createCA(ctx)
if err != nil {
return nil, nil, nil, err
}

// Then create the private key for the serving cert
servKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
glog.Warningf("error generating random key: %s", err)
logger.Error("error generating random key", zap.Error(err))
return nil, nil, nil, err
}
servCertTemplate, err := createServerCertTemplate()
if err != nil {
glog.Warningf("failed to create the server certificate template: %s", err)
logger.Error("failed to create the server certificate template", zap.Error(err))
return nil, nil, nil, err
}

// create a certificate which wraps the server's public key, sign it with the CA private key
_, servCertPEM, err := createCert(servCertTemplate, caCertificate, &servKey.PublicKey, caKey)
if err != nil {
glog.Warningf("error signing server certificate template: %s", err)
logger.Error("error signing server certificate template", zap.Error(err))
return nil, nil, nil, err
}
servKeyPEM := pem.EncodeToMemory(&pem.Block{
Expand Down
48 changes: 28 additions & 20 deletions pkg/webhook/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ limitations under the License.
package webhook

import (
"context"
"errors"
"fmt"
"path"
"reflect"
"strings"

"github.com/elafros/elafros/pkg/apis/ela/v1alpha1"
"github.com/golang/glog"
"github.com/elafros/elafros/pkg/logging"
"github.com/mattbaird/jsonpatch"
corev1 "k8s.io/api/core/v1"
)
Expand All @@ -40,19 +41,21 @@ var (
errEmptySpecInConfiguration = errMissingField("spec")
errEmptyRevisionTemplateInSpec = errMissingField("spec.revisionTemplate")
errEmptyContainerInRevisionTemplate = errMissingField("spec.revisionTemplate.spec.container")
errInvalidConfigurationInput = errors.New("Failed to convert input into Configuration.")
errInvalidConfigurationInput = errors.New("failed to convert input into Configuration")
)

// ValidateConfiguration is Configuration resource specific validation and mutation handler
func ValidateConfiguration(patches *[]jsonpatch.JsonPatchOperation, old GenericCRD, new GenericCRD) error {
_, newConfiguration, err := unmarshalConfigurations(old, new, "ValidateConfiguration")
if err != nil {
return err
}
if err := validateConfiguration(newConfiguration); err != nil {
return err
func ValidateConfiguration(ctx context.Context) ResourceCallback {
return func(patches *[]jsonpatch.JsonPatchOperation, old GenericCRD, new GenericCRD) error {
_, newConfiguration, err := unmarshalConfigurations(ctx, old, new, "ValidateConfiguration")
if err != nil {
return err
}
if err := validateConfiguration(newConfiguration); err != nil {
return err
}
return nil
}
return nil
}

func validateConfiguration(configuration *v1alpha1.Configuration) error {
Expand Down Expand Up @@ -122,16 +125,19 @@ func validateContainer(container corev1.Container) error {
return nil
}

func SetConfigurationDefaults(patches *[]jsonpatch.JsonPatchOperation, crd GenericCRD) error {
_, config, err := unmarshalConfigurations(nil, crd, "SetConfigurationDefaults")
if err != nil {
return err
}
// SetConfigurationDefaults set defaults on an configurations.
func SetConfigurationDefaults(ctx context.Context) ResourceDefaulter {
return func(patches *[]jsonpatch.JsonPatchOperation, crd GenericCRD) error {
_, config, err := unmarshalConfigurations(ctx, nil, crd, "SetConfigurationDefaults")
if err != nil {
return err
}

return SetConfigurationSpecDefaults(patches, "/spec", config.Spec)
return setConfigurationSpecDefaults(patches, "/spec", config.Spec)
}
}

func SetConfigurationSpecDefaults(patches *[]jsonpatch.JsonPatchOperation, patchBase string, spec v1alpha1.ConfigurationSpec) error {
func setConfigurationSpecDefaults(patches *[]jsonpatch.JsonPatchOperation, patchBase string, spec v1alpha1.ConfigurationSpec) error {
if spec.RevisionTemplate.Spec.ConcurrencyModel == "" {
*patches = append(*patches, jsonpatch.JsonPatchOperation{
Operation: "add",
Expand All @@ -142,7 +148,9 @@ func SetConfigurationSpecDefaults(patches *[]jsonpatch.JsonPatchOperation, patch
return nil
}

func unmarshalConfigurations(old GenericCRD, new GenericCRD, fnName string) (*v1alpha1.Configuration, *v1alpha1.Configuration, error) {
func unmarshalConfigurations(
ctx context.Context, old GenericCRD, new GenericCRD, fnName string) (*v1alpha1.Configuration, *v1alpha1.Configuration, error) {
logger := logging.FromContext(ctx)
var oldConfiguration *v1alpha1.Configuration
if old != nil {
var ok bool
Expand All @@ -151,13 +159,13 @@ func unmarshalConfigurations(old GenericCRD, new GenericCRD, fnName string) (*v1
return nil, nil, errInvalidConfigurationInput
}
}
glog.Infof("%s: OLD Configuration is\n%+v", fnName, oldConfiguration)
logger.Infof("%s: OLD Configuration is\n%+v", fnName, oldConfiguration)

newConfiguration, ok := new.(*v1alpha1.Configuration)
if !ok {
return nil, nil, errInvalidConfigurationInput
}
glog.Infof("%s: NEW Configuration is\n%+v", fnName, newConfiguration)
logger.Infof("%s: NEW Configuration is\n%+v", fnName, newConfiguration)

return oldConfiguration, newConfiguration, nil
}
Loading