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

Have the Webhook react to pod creation/update only #2472

Merged
merged 7 commits into from
Mar 26, 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
8 changes: 7 additions & 1 deletion chart/templates/proxy_injector.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,13 @@ rules:
verbs: ["create", "get", "delete"]
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get"]
verbs: ["list", "get", "watch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["list"]
alpeb marked this conversation as resolved.
Show resolved Hide resolved
- apiGroups: ["apps"]
resources: ["replicasets"]
verbs: ["list", "get", "watch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
Expand Down
6 changes: 5 additions & 1 deletion cli/cmd/inject.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,11 @@ func (rt resourceTransformerInject) transform(bytes []byte) ([]byte, []inject.Re
if patchJSON == nil {
return bytes, reports, nil
}
log.Infof("patch generated for: %s", conf)
// TODO: refactor GetPatch() for it to return just one report item
if len(reports) > 0 {
r := reports[0]
log.Infof("patch generated for: %s", r.ResName())
}
log.Debugf("patch: %s", patchJSON)
patch, err := jsonpatch.DecodePatch(patchJSON)
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1491,7 +1491,13 @@ rules:
verbs: ["create", "get", "delete"]
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get"]
verbs: ["list", "get", "watch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["list"]
- apiGroups: ["apps"]
resources: ["replicasets"]
verbs: ["list", "get", "watch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
Expand Down
8 changes: 7 additions & 1 deletion cli/cmd/testdata/install_output.golden
Original file line number Diff line number Diff line change
Expand Up @@ -1424,7 +1424,13 @@ rules:
verbs: ["create", "get", "delete"]
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get"]
verbs: ["list", "get", "watch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["list"]
- apiGroups: ["apps"]
resources: ["replicasets"]
verbs: ["list", "get", "watch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
Expand Down
10 changes: 6 additions & 4 deletions controller/cmd/proxy-injector/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,17 @@ func main() {
defer close(stop)
signal.Notify(stop, os.Interrupt, os.Kill)

k8sClient, err := k8s.NewClientSet(*kubeconfig)
k8sAPI, err := k8s.InitializeAPI(*kubeconfig, k8s.NS, k8s.RS)
if err != nil {
log.Fatalf("failed to initialize Kubernetes client: %s", err)
log.Fatalf("failed to initialize Kubernetes API: %s", err)
}

rootCA, err := tls.GenerateRootCAWithDefaults("Proxy Injector Mutating Webhook Admission Controller CA")
if err != nil {
log.Fatalf("failed to create root CA: %s", err)
}

webhookConfig, err := injector.NewWebhookConfig(k8sClient, *controllerNamespace, *webhookServiceName, rootCA)
webhookConfig, err := injector.NewWebhookConfig(k8sAPI, *controllerNamespace, *webhookServiceName, rootCA)
if err != nil {
log.Fatalf("failed to read the trust anchor file: %s", err)
}
Expand All @@ -48,11 +48,13 @@ func main() {
}
log.Infof("created mutating webhook configuration: %s", mwc.ObjectMeta.SelfLink)

s, err := injector.NewWebhookServer(k8sClient, *addr, *controllerNamespace, *noInitContainer, rootCA)
s, err := injector.NewWebhookServer(k8sAPI, *addr, *controllerNamespace, *noInitContainer, rootCA)
if err != nil {
log.Fatalf("failed to initialize the webhook server: %s", err)
}

k8sAPI.Sync()

go func() {
log.Infof("listening at %s", *addr)
if err := s.ListenAndServeTLS("", ""); err != nil {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
name: linkerd-secrets
secret:
secretName: nginx-deployment-tls-linkerd-io
optional: true
6 changes: 3 additions & 3 deletions controller/proxy-injector/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import (
"io/ioutil"
"net/http"

"github.com/linkerd/linkerd2/controller/k8s"
pkgTls "github.com/linkerd/linkerd2/pkg/tls"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/kubernetes"
)

// WebhookServer is the webhook's HTTP server. It has an embedded webhook which
Expand All @@ -21,7 +21,7 @@ type WebhookServer struct {
}

// NewWebhookServer returns a new instance of the WebhookServer.
func NewWebhookServer(client kubernetes.Interface, addr, controllerNamespace string, noInitContainer bool, rootCA *pkgTls.CA) (*WebhookServer, error) {
func NewWebhookServer(api *k8s.API, addr, controllerNamespace string, noInitContainer bool, rootCA *pkgTls.CA) (*WebhookServer, error) {
c, err := tlsConfig(rootCA, controllerNamespace)
if err != nil {
return nil, err
Expand All @@ -32,7 +32,7 @@ func NewWebhookServer(client kubernetes.Interface, addr, controllerNamespace str
TLSConfig: c,
}

webhook, err := NewWebhook(client, controllerNamespace, noInitContainer)
webhook, err := NewWebhook(api, controllerNamespace, noInitContainer)
if err != nil {
return nil, err
}
Expand Down
21 changes: 12 additions & 9 deletions controller/proxy-injector/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"reflect"
"testing"

"github.com/linkerd/linkerd2/controller/k8s"
"github.com/linkerd/linkerd2/controller/proxy-injector/fake"
"github.com/linkerd/linkerd2/pkg/tls"
log "github.com/sirupsen/logrus"
Expand All @@ -20,9 +21,12 @@ var (

func init() {
// create a webhook which uses its fake client to seed the sidecar configmap
fakeClient := fake.NewClient("")
k8sAPI, err := k8s.NewFakeAPI()
if err != nil {
panic(err)
}

webhook, err := NewWebhook(fakeClient, fake.DefaultControllerNamespace, false)
webhook, err := NewWebhook(k8sAPI, fake.DefaultControllerNamespace, false)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -73,13 +77,12 @@ func TestNewWebhookServer(t *testing.T) {
log.Fatalf("failed to create root CA: %s", err)
}

var (
addr = ":7070"
kubeconfig = ""
)
fakeClient := fake.NewClient(kubeconfig)

server, err := NewWebhookServer(fakeClient, addr, fake.DefaultControllerNamespace, false, rootCA)
addr := ":7070"
k8sAPI, err := k8s.NewFakeAPI()
if err != nil {
t.Fatalf("NewFakeAPI returned an error: %s", err)
}
server, err := NewWebhookServer(k8sAPI, addr, fake.DefaultControllerNamespace, false, rootCA)
if err != nil {
t.Fatal("Unexpected error: ", err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ webhooks:
caBundle: {{ .CABundle }}
rules:
- operations: [ "CREATE" , "UPDATE" ]
apiGroups: ["apps", "extensions"]
apiVersions: ["v1", "v1beta1", "v1beta2"]
resources: ["deployments"]`
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]`
39 changes: 23 additions & 16 deletions controller/proxy-injector/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,39 @@ import (
"fmt"

pb "github.com/linkerd/linkerd2/controller/gen/config"
"github.com/linkerd/linkerd2/controller/k8s"
"github.com/linkerd/linkerd2/pkg/config"
"github.com/linkerd/linkerd2/pkg/inject"
"github.com/linkerd/linkerd2/pkg/k8s"
pkgK8s "github.com/linkerd/linkerd2/pkg/k8s"
"github.com/linkerd/linkerd2/pkg/version"
log "github.com/sirupsen/logrus"
admissionv1beta1 "k8s.io/api/admission/v1beta1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/kubernetes"
"sigs.k8s.io/yaml"
)

// Webhook is a Kubernetes mutating admission webhook that mutates pods admission
// requests by injecting sidecar container spec into the pod spec during pod
// creation.
type Webhook struct {
client kubernetes.Interface
k8sAPI *k8s.API
deserializer runtime.Decoder
controllerNamespace string
noInitContainer bool
}

// NewWebhook returns a new instance of Webhook.
func NewWebhook(client kubernetes.Interface, controllerNamespace string, noInitContainer bool) (*Webhook, error) {
func NewWebhook(api *k8s.API, controllerNamespace string, noInitContainer bool) (*Webhook, error) {
var (
scheme = runtime.NewScheme()
codecs = serializer.NewCodecFactory(scheme)
)

return &Webhook{
client: client,
k8sAPI: api,
deserializer: codecs.UniversalDeserializer(),
controllerNamespace: controllerNamespace,
noInitContainer: noInitContainer,
Expand Down Expand Up @@ -87,24 +88,25 @@ func (w *Webhook) decode(data []byte) (*admissionv1beta1.AdmissionReview, error)
func (w *Webhook) inject(request *admissionv1beta1.AdmissionRequest) (*admissionv1beta1.AdmissionResponse, error) {
log.Debugf("request object bytes: %s", request.Object.Raw)

globalConfig, err := config.Global(k8s.MountPathGlobalConfig)
globalConfig, err := config.Global(pkgK8s.MountPathGlobalConfig)
if err != nil {
return nil, err
}

proxyConfig, err := config.Proxy(k8s.MountPathProxyConfig)
proxyConfig, err := config.Proxy(pkgK8s.MountPathProxyConfig)
if err != nil {
return nil, err
}

namespace, err := w.client.CoreV1().Namespaces().Get(request.Namespace, metav1.GetOptions{})
namespace, err := w.k8sAPI.NS().Lister().Get(request.Namespace)
if err != nil {
return nil, err
}
nsAnnotations := namespace.GetAnnotations()

configs := &pb.All{Global: globalConfig, Proxy: proxyConfig}
conf := inject.NewResourceConfig(configs).
WithOwnerRetriever(w.ownerRetriever(request.Namespace)).
WithNsAnnotations(nsAnnotations).
WithKind(request.Kind.Kind)
nonEmpty, err := conf.ParseMeta(request.Object.Raw)
Expand All @@ -120,7 +122,7 @@ func (w *Webhook) inject(request *admissionv1beta1.AdmissionRequest) (*admission
return admissionResponse, nil
}

p, _, err := conf.GetPatch(request.Object.Raw, inject.ShouldInjectWebhook)
p, reports, err := conf.GetPatch(request.Object.Raw, inject.ShouldInjectWebhook)
if err != nil {
return nil, err
}
Expand All @@ -131,17 +133,15 @@ func (w *Webhook) inject(request *admissionv1beta1.AdmissionRequest) (*admission

p.AddCreatedByPodAnnotation(fmt.Sprintf("linkerd/proxy-injector %s", version.Version))

// When adding workloads through `kubectl apply` the spec template labels are
// automatically copied to the workload's main metadata section.
// This doesn't happen when adding labels through the webhook. So we manually
// add them to remain consistent.
conf.AddRootLabels(p)

patchJSON, err := p.Marshal()
if err != nil {
return nil, err
}
log.Infof("patch generated for: %s", conf)
// TODO: refactor GetPatch() so it only returns one report item
if len(reports) > 0 {
r := reports[0]
log.Infof("patch generated for: %s", r.ResName())
}
log.Debugf("patch: %s", patchJSON)

patchType := admissionv1beta1.PatchTypeJSONPatch
Expand All @@ -150,3 +150,10 @@ func (w *Webhook) inject(request *admissionv1beta1.AdmissionRequest) (*admission

return admissionResponse, nil
}

func (w *Webhook) ownerRetriever(ns string) inject.OwnerRetrieverFunc {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

who said go cannot haz curry? 🐱

return func(p *v1.Pod) (string, string) {
p.SetNamespace(ns)
return w.k8sAPI.GetOwnerKindAndName(p)
}
}
19 changes: 11 additions & 8 deletions controller/proxy-injector/webhook_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import (
"encoding/base64"
"text/template"

"github.com/linkerd/linkerd2/controller/k8s"
"github.com/linkerd/linkerd2/controller/proxy-injector/tmpl"
k8sPkg "github.com/linkerd/linkerd2/pkg/k8s"
"github.com/linkerd/linkerd2/pkg/tls"
log "github.com/sirupsen/logrus"
arv1beta1 "k8s.io/api/admissionregistration/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"sigs.k8s.io/yaml"
)

Expand All @@ -22,11 +22,11 @@ type WebhookConfig struct {
webhookServiceName string
trustAnchor []byte
configTemplate *template.Template
k8sAPI kubernetes.Interface
k8sAPI *k8s.API
}

// NewWebhookConfig returns a new instance of initiator.
func NewWebhookConfig(client kubernetes.Interface, controllerNamespace, webhookServiceName string, rootCA *tls.CA) (*WebhookConfig, error) {
func NewWebhookConfig(api *k8s.API, controllerNamespace, webhookServiceName string, rootCA *tls.CA) (*WebhookConfig, error) {
trustAnchor := rootCA.Cred.EncodeCertificatePEM()

t := template.New(k8sPkg.ProxyInjectorWebhookConfig)
Expand All @@ -36,7 +36,7 @@ func NewWebhookConfig(client kubernetes.Interface, controllerNamespace, webhookS
webhookServiceName: webhookServiceName,
trustAnchor: []byte(trustAnchor),
configTemplate: template.Must(t.Parse(tmpl.MutatingWebhookConfigurationSpec)),
k8sAPI: client,
k8sAPI: api,
}, nil
}

Expand Down Expand Up @@ -97,15 +97,18 @@ func (w *WebhookConfig) create() (*arv1beta1.MutatingWebhookConfiguration, error
return nil, err
}

return w.k8sAPI.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&config)
return w.k8sAPI.Client.
AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&config)
}

func (w *WebhookConfig) get() (*arv1beta1.MutatingWebhookConfiguration, error) {
return w.k8sAPI.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().
return w.k8sAPI.Client.
AdmissionregistrationV1beta1().MutatingWebhookConfigurations().
Get(k8sPkg.ProxyInjectorWebhookConfig, metav1.GetOptions{})
}

func (w *WebhookConfig) delete() error {
return w.k8sAPI.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Delete(
k8sPkg.ProxyInjectorWebhookConfig, &metav1.DeleteOptions{})
return w.k8sAPI.Client.
AdmissionregistrationV1beta1().MutatingWebhookConfigurations().
Delete(k8sPkg.ProxyInjectorWebhookConfig, &metav1.DeleteOptions{})
}
8 changes: 6 additions & 2 deletions controller/proxy-injector/webhook_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"log"
"testing"

"github.com/linkerd/linkerd2/controller/k8s"
"github.com/linkerd/linkerd2/controller/proxy-injector/fake"
"github.com/linkerd/linkerd2/pkg/tls"
)
Expand All @@ -16,14 +17,17 @@ func TestCreate(t *testing.T) {
)
log.SetOutput(ioutil.Discard)

client := fake.NewClient("")
k8sAPI, err := k8s.NewFakeAPI()
if err != nil {
t.Fatalf("NewFakeAPI returned an error: %s", err)
}

rootCA, err := tls.GenerateRootCAWithDefaults("Test CA")
if err != nil {
t.Fatalf("failed to create root CA: %s", err)
}

webhookConfig, err := NewWebhookConfig(client, namespace, webhookServiceName, rootCA)
webhookConfig, err := NewWebhookConfig(k8sAPI, namespace, webhookServiceName, rootCA)
if err != nil {
t.Fatal("Unexpected error: ", err)
}
Expand Down
Loading