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

Have the Webhook react to pod creation/update only

This was already working almost out-of-the-box, just had to:

- Change the webhook config so it watches pods instead of deployments
- Grant some extra ClusterRole permissions
- Add the piece that figures what's the OwnerReference and add the label
for it
- Manually inject service account mount paths
- Readd volumes tests

Fixes #2342 and #1751

Signed-off-by: Alejandro Pedraza <alejandro@buoyant.io>
This commit is contained in:
Alejandro Pedraza 2019-03-26 11:53:56 -05:00 committed by GitHub
parent 9c442f6885
commit 7efe385feb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 205 additions and 121 deletions

View File

@ -80,7 +80,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

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -27,9 +27,9 @@ 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")
@ -37,7 +37,7 @@ func main() {
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)
}
@ -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 {

View File

@ -0,0 +1,4 @@
name: linkerd-secrets
secret:
secretName: nginx-deployment-tls-linkerd-io
optional: true

View File

@ -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
@ -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
@ -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
}

View File

@ -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"
@ -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)
}
@ -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)
}

View File

@ -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"]`

View File

@ -4,16 +4,17 @@ 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"
)
@ -21,21 +22,21 @@ import (
// 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,
@ -87,17 +88,17 @@ 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
}
@ -105,6 +106,7 @@ func (w *Webhook) inject(request *admissionv1beta1.AdmissionRequest) (*admission
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)
@ -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
}
@ -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
@ -150,3 +150,10 @@ func (w *Webhook) inject(request *admissionv1beta1.AdmissionRequest) (*admission
return admissionResponse, nil
}
func (w *Webhook) ownerRetriever(ns string) inject.OwnerRetrieverFunc {
return func(p *v1.Pod) (string, string) {
p.SetNamespace(ns)
return w.k8sAPI.GetOwnerKindAndName(p)
}
}

View File

@ -5,6 +5,7 @@ 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"
@ -12,7 +13,6 @@ import (
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"
)
@ -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)
@ -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
}
@ -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{})
}

View File

@ -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"
)
@ -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)
}

View File

@ -7,7 +7,7 @@ import (
"github.com/linkerd/linkerd2/controller/gen/config"
"github.com/linkerd/linkerd2/controller/proxy-injector/fake"
"github.com/linkerd/linkerd2/pkg/inject"
"github.com/linkerd/linkerd2/pkg/k8s"
pkgK8s "github.com/linkerd/linkerd2/pkg/k8s"
admissionv1beta1 "k8s.io/api/admission/v1beta1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -42,7 +42,7 @@ var (
func confNsEnabled() *inject.ResourceConfig {
return inject.
NewResourceConfig(configs).
WithNsAnnotations(map[string]string{k8s.ProxyInjectAnnotation: k8s.ProxyInjectEnabled})
WithNsAnnotations(map[string]string{pkgK8s.ProxyInjectAnnotation: pkgK8s.ProxyInjectEnabled})
}
func confNsDisabled() *inject.ResourceConfig {

View File

@ -73,6 +73,10 @@ var injectableKinds = []string{
k8s.StatefulSet,
}
// OwnerRetrieverFunc is a function that returns a pod's owner reference
// kind and name
type OwnerRetrieverFunc func(*v1.Pod) (string, string)
// ResourceConfig contains the parsed information for a given workload
type ResourceConfig struct {
configs *config.All
@ -80,6 +84,7 @@ type ResourceConfig struct {
destinationDNSOverride string
identityDNSOverride string
proxyOutboundCapacity map[string]uint
ownerRetriever OwnerRetrieverFunc
workload struct {
obj runtime.Object
@ -108,20 +113,6 @@ func NewResourceConfig(configs *config.All) *ResourceConfig {
return config
}
// String satisfies the Stringer interface
func (conf *ResourceConfig) String() string {
l := []string{}
if conf.workload.metaType.Kind != "" {
l = append(l, conf.workload.metaType.Kind)
}
if conf.workload.meta != nil {
l = append(l, fmt.Sprintf("%s.%s", conf.workload.meta.GetName(), conf.workload.meta.GetNamespace()))
}
return strings.Join(l, "/")
}
// WithKind enriches ResourceConfig with the workload kind
func (conf *ResourceConfig) WithKind(kind string) *ResourceConfig {
conf.workload.metaType = metav1.TypeMeta{Kind: kind}
@ -143,6 +134,13 @@ func (conf *ResourceConfig) WithProxyOutboundCapacity(m map[string]uint) *Resour
return conf
}
// WithOwnerRetriever enriches ResourceConfig with a function that allows to retrieve
// the kind and name of the workload's owner reference
func (conf *ResourceConfig) WithOwnerRetriever(f OwnerRetrieverFunc) *ResourceConfig {
conf.ownerRetriever = f
return conf
}
// YamlMarshalObj returns the yaml for the workload in conf
func (conf *ResourceConfig) YamlMarshalObj() ([]byte, error) {
return yaml.Marshal(conf.workload.obj)
@ -158,7 +156,7 @@ func (conf *ResourceConfig) ParseMetaAndYaml(bytes []byte) (*Report, error) {
}
// ParseMeta extracts metadata from bytes.
// It returns false if the workload's payload is empty
// It returns false if the workload's payload is empty.
func (conf *ResourceConfig) ParseMeta(bytes []byte) (bool, error) {
if err := yaml.Unmarshal(bytes, &conf.workload.metaType); err != nil {
return false, err
@ -181,12 +179,7 @@ func (conf *ResourceConfig) GetPatch(
return nil, nil, err
}
var patch *Patch
if strings.ToLower(conf.workload.metaType.Kind) == k8s.Pod {
patch = NewPatchPod()
} else {
patch = NewPatchDeployment()
}
patch := NewPatch(conf.workload.metaType.Kind)
// If we don't inject anything into the pod template then output the
// original serialization of the original object. Otherwise, output the
@ -246,6 +239,8 @@ func (conf *ResourceConfig) JSONToYAML(bytes []byte) ([]byte, error) {
return yaml.Marshal(obj)
}
// parse parses the bytes payload, filling the gaps in ResourceConfig
// depending on the workload kind
func (conf *ResourceConfig) parse(bytes []byte) error {
// The Kubernetes API is versioned and each version has an API modeled
// with its own distinct Go types. If we tell `yaml.Unmarshal()` which
@ -352,6 +347,24 @@ func (conf *ResourceConfig) parse(bytes []byte) error {
conf.workload.obj = v
conf.pod.spec = &v.Spec
conf.pod.Meta = &v.ObjectMeta
if conf.ownerRetriever != nil {
kind, name := conf.ownerRetriever(v)
switch kind {
case k8s.Deployment:
conf.pod.labels[k8s.ProxyDeploymentLabel] = name
case k8s.ReplicationController:
conf.pod.labels[k8s.ProxyReplicationControllerLabel] = name
case k8s.ReplicaSet:
conf.pod.labels[k8s.ProxyReplicaSetLabel] = name
case k8s.Job:
conf.pod.labels[k8s.ProxyJobLabel] = name
case k8s.DaemonSet:
conf.pod.labels[k8s.ProxyDaemonSetLabel] = name
case k8s.StatefulSet:
conf.pod.labels[k8s.ProxyStatefulSetLabel] = name
}
}
}
return nil
@ -364,8 +377,9 @@ func (conf *ResourceConfig) complete(template *v1.PodTemplateSpec) {
// injectPodSpec adds linkerd sidecars to the provided PodSpec.
func (conf *ResourceConfig) injectPodSpec(patch *Patch) {
saVolumeMount := conf.serviceAccountVolumeMount()
if !conf.configs.GetGlobal().GetCniEnabled() {
conf.injectProxyInit(patch)
conf.injectProxyInit(patch, saVolumeMount)
}
proxyUID := conf.proxyUID()
@ -455,6 +469,10 @@ func (conf *ResourceConfig) injectPodSpec(patch *Patch) {
}
}
if saVolumeMount != nil {
sidecar.VolumeMounts = []v1.VolumeMount{*saVolumeMount}
}
idctx := conf.configs.GetGlobal().GetIdentityContext()
if idctx == nil {
sidecar.Env = append(sidecar.Env, v1.EnvVar{
@ -527,7 +545,7 @@ func (conf *ResourceConfig) injectPodSpec(patch *Patch) {
patch.addContainer(&sidecar)
}
func (conf *ResourceConfig) injectProxyInit(patch *Patch) {
func (conf *ResourceConfig) injectProxyInit(patch *Patch, saVolumeMount *v1.VolumeMount) {
nonRoot := false
runAsUser := int64(0)
initContainer := &v1.Container{
@ -545,12 +563,28 @@ func (conf *ResourceConfig) injectProxyInit(patch *Patch) {
RunAsUser: &runAsUser,
},
}
if saVolumeMount != nil {
initContainer.VolumeMounts = []v1.VolumeMount{*saVolumeMount}
}
if len(conf.pod.spec.InitContainers) == 0 {
patch.addInitContainerRoot()
}
patch.addInitContainer(initContainer)
}
func (conf *ResourceConfig) serviceAccountVolumeMount() *v1.VolumeMount {
// Probably always true, but wanna be super-safe
if containers := conf.pod.spec.Containers; len(containers) > 0 {
for _, vm := range containers[0].VolumeMounts {
if vm.MountPath == k8s.MountPathServiceAccount {
vm := vm // pin
return &vm
}
}
}
return nil
}
// Given a ObjectMeta, update ObjectMeta in place with the new labels and
// annotations.
func (conf *ResourceConfig) injectObjectMeta(patch *Patch) {
@ -565,15 +599,13 @@ func (conf *ResourceConfig) injectObjectMeta(patch *Patch) {
patch.addPodAnnotation(k8s.IdentityModeAnnotation, k8s.IdentityModeDisabled)
}
for k, v := range conf.pod.labels {
patch.addPodLabel(k, v)
}
}
// AddRootLabels adds all the pod labels into the root workload (e.g. Deployment)
func (conf *ResourceConfig) AddRootLabels(patch *Patch) {
for k, v := range conf.pod.labels {
patch.addRootLabel(k, v)
if len(conf.pod.labels) > 0 {
if len(conf.pod.Meta.Labels) == 0 {
patch.addPodLabelsRoot()
}
for k, v := range conf.pod.labels {
patch.addPodLabel(k, v)
}
}
}

View File

@ -227,7 +227,7 @@ func TestConfigAccessors(t *testing.T) {
t.Fatal(err)
}
if err := resourceConfig.parse(data); err != nil {
if _, err := resourceConfig.ParseMetaAndYaml(data); err != nil {
t.Fatal(err)
}

View File

@ -24,8 +24,21 @@ type Patch struct {
patchPathPodAnnotations string
}
// NewPatchDeployment returns a new instance of Patch for Deployment-like workloads
func NewPatchDeployment() *Patch {
// NewPatch returns a new instance of Patch for any kind of workload kind
func NewPatch(kind string) *Patch {
if strings.ToLower(kind) == k8s.Pod {
return &Patch{
patchOps: []*patchOp{},
patchPathContainer: "/spec/containers/-",
patchPathInitContainerRoot: "/spec/initContainers",
patchPathInitContainer: "/spec/initContainers/-",
patchPathVolumeRoot: "/spec/volumes",
patchPathVolume: "/spec/volumes/-",
patchPathPodLabels: patchPathRootLabels,
patchPathPodAnnotations: "/metadata/annotations",
}
}
return &Patch{
patchOps: []*patchOp{},
patchPathContainer: "/spec/template/spec/containers/-",
@ -38,20 +51,6 @@ func NewPatchDeployment() *Patch {
}
}
// NewPatchPod returns a new instance of Patch for Pod workloads
func NewPatchPod() *Patch {
return &Patch{
patchOps: []*patchOp{},
patchPathContainer: "/spec/containers/-",
patchPathInitContainerRoot: "/spec/initContainers",
patchPathInitContainer: "/spec/initContainers/-",
patchPathVolumeRoot: "/spec/volumes",
patchPathVolume: "/spec/volumes/-",
patchPathPodLabels: patchPathRootLabels,
patchPathPodAnnotations: "/metadata/annotations",
}
}
// Marshal returns the patch as JSON
func (p *Patch) Marshal() ([]byte, error) {
return json.Marshal(p.patchOps)
@ -97,6 +96,15 @@ func (p *Patch) addVolume(volume *corev1.Volume) {
})
}
func (p *Patch) addPodLabelsRoot() {
p.patchOps = append(p.patchOps, &patchOp{
Op: "add",
Path: p.patchPathPodLabels,
Value: map[string]string{},
})
}
// AddPodLabel appends the label with the provided key and value
func (p *Patch) addPodLabel(key, value string) {
p.patchOps = append(p.patchOps, &patchOp{
Op: "add",
@ -105,14 +113,6 @@ func (p *Patch) addPodLabel(key, value string) {
})
}
func (p *Patch) addRootLabel(key, value string) {
p.patchOps = append(p.patchOps, &patchOp{
Op: "add",
Path: patchPathRootLabels + "/" + escapeKey(key),
Value: value,
})
}
func (p *Patch) addPodAnnotationsRoot() {
p.patchOps = append(p.patchOps, &patchOp{
Op: "add",

View File

@ -13,11 +13,10 @@ import (
func TestPatch(t *testing.T) {
fixture := fake.NewFactory(filepath.Join("..", "..", "controller", "proxy-injector", "fake", "data"))
// TODO end entity volume
// secrets, err := fixture.Volume("inject-linkerd-secrets-volume-spec.yaml")
// if err != nil {
// t.Fatal("Unexpected error: ", err)
// }
secrets, err := fixture.Volume("inject-linkerd-secrets-volume-spec.yaml")
if err != nil {
t.Fatal("Unexpected error: ", err)
}
sidecar, err := fixture.Container("inject-sidecar-container-spec.yaml")
if err != nil {
@ -34,22 +33,22 @@ func TestPatch(t *testing.T) {
createdBy = "linkerd/cli v18.8.4"
)
actual := NewPatchDeployment()
actual := NewPatch(k8sPkg.Deployment)
actual.addContainer(sidecar)
actual.addInitContainerRoot()
actual.addInitContainer(init)
//actual.addVolumeRoot()
//actual.addVolume(secrets)
actual.addVolumeRoot()
actual.addVolume(secrets)
actual.addPodLabel(k8sPkg.ControllerNSLabel, controllerNamespace)
actual.addPodAnnotation(k8sPkg.CreatedByAnnotation, createdBy)
expected := NewPatchDeployment()
expected := NewPatch(k8sPkg.Deployment)
expected.patchOps = []*patchOp{
{Op: "add", Path: expected.patchPathContainer, Value: sidecar},
{Op: "add", Path: expected.patchPathInitContainerRoot, Value: []*v1.Container{}},
{Op: "add", Path: expected.patchPathInitContainer, Value: init},
//{Op: "add", Path: expected.patchPathVolumeRoot, Value: []*v1.Volume{}},
//{Op: "add", Path: expected.patchPathVolume, Value: secrets},
{Op: "add", Path: expected.patchPathVolumeRoot, Value: []*v1.Volume{}},
{Op: "add", Path: expected.patchPathVolume, Value: secrets},
{Op: "add", Path: expected.patchPathPodLabels + "/" + escapeKey(k8sPkg.ControllerNSLabel), Value: controllerNamespace},
{Op: "add", Path: expected.patchPathPodAnnotations + "/" + escapeKey(k8sPkg.CreatedByAnnotation), Value: createdBy},
}

View File

@ -27,7 +27,11 @@ func newReport(conf *ResourceConfig) Report {
var name string
if m := conf.pod.Meta; m != nil {
name = m.Name
if name == "" {
name = m.GenerateName
}
}
return Report{
Kind: strings.ToLower(conf.workload.metaType.Kind),
Name: name,
@ -35,13 +39,13 @@ func newReport(conf *ResourceConfig) Report {
}
// ResName returns a string "Kind/Name" for the workload referred in the report r
func (r Report) ResName() string {
func (r *Report) ResName() string {
return fmt.Sprintf("%s/%s", r.Kind, r.Name)
}
// Injectable returns false if the report flags indicate that the workload is on a host network
// or there is already a sidecar or the resource is not supported or inject is explicitly disabled
func (r Report) Injectable() bool {
func (r *Report) Injectable() bool {
return !r.HostNetwork && !r.Sidecar && !r.UnsupportedResource && !r.InjectDisabled
}

View File

@ -190,6 +190,10 @@ const (
// MountPathBase is the base directory of the mount path.
MountPathBase = "/var/run/linkerd"
// MountPathServiceAccount is the default path where Kuberenetes stores
// the service account token
MountPathServiceAccount = "/var/run/secrets/kubernetes.io/serviceaccount"
// MountPathGlobalConfig is the path at which the global config file is mounted.
MountPathGlobalConfig = MountPathBase + "/config/global"