Skip to content

Commit

Permalink
Use structured logging in webhook. (#978)
Browse files Browse the repository at this point in the history
  • Loading branch information
mdemirhan authored and google-prow-robot committed May 29, 2018
1 parent 83be961 commit 876a45d
Show file tree
Hide file tree
Showing 18 changed files with 392 additions and 237 deletions.
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

0 comments on commit 876a45d

Please sign in to comment.