mirror of https://github.com/linkerd/linkerd2.git
Proxy init and sidecar containers auto-injection (#1714)
* Support auto sidecar-injection 1. Add proxy-injector deployment spec to cli/install/template.go 2. Inject the Linkerd CA bundle into the MutatingWebhookConfiguration during the webhook's start-up process. 3. Add a new handler to the CA controller to create a new secret for the webhook when a new MutatingWebhookConfiguration is created. 4. Declare a config map to store the proxy and proxy-init container specs used during the auto-inject process. 5. Ignore namespace and pods that are labeled with linkerd.io/auto-inject: disabled or linkerd.io/auto-inject: completed 6. Add new flag to `linkerd install` to enable/disable proxy auto-injection Proposed implementation for #561. * Resolve missing packages errors * Move the auto-inject label to the pod level * PR review items * Move proxy-injector to its own deployment * Ignore pods that already have proxy injected This ensures the webhook doesn't error out due to proxy that are injected using the command * PR review items on creating/updating the MWC on-start * Replace API calls to ConfigMap with file reads * Fixed post-rebase broken tests * Don't mutate the auto-inject label Since we started using healhcheck.HasExistingSidecars() to ensure pods with existing proxies aren't mutated, we don't need to use the auto-inject label as an indicator. This resolves a bug which happens with the kubectl run command where the deployment is also assigned the auto-inject label. The mutation causes the pod auto-inject label to not match the deployment label, causing kubectl run to fail. * Tidy up unit tests * Include proxy resource requests in sidecar config map * Fixes to broken YAML in CLI install config The ignore inbound and outbound ports are changed to string type to avoid broken YAML caused by the string conversion in the uint slice. Also, parameterized the proxy bind timeout option in template.go. Renamed the sidecar config map to 'linkerd-proxy-injector-webhook-config'. Signed-off-by: ihcsim <ihcsim@gmail.com>
This commit is contained in:
parent
db37c5a007
commit
4fba6aca0a
|
|
@ -13,3 +13,4 @@ web/app/yarn-error.log
|
|||
.protoc
|
||||
.gorun
|
||||
.dep*
|
||||
**/*.swp
|
||||
|
|
|
|||
|
|
@ -533,9 +533,10 @@
|
|||
revision = "d670f9405373e636a5a2765eea47fac0c9bc91a4"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:34ffbf9ed5e63a11e4e0aaab597dc36c552da8b5b6bd49d8f73dadd4afd7e677"
|
||||
digest = "1:728c0c37966b7a6c8980fab69a3d690cc0996e206804a55052318858d00f1e17"
|
||||
name = "k8s.io/api"
|
||||
packages = [
|
||||
"admission/v1beta1",
|
||||
"admissionregistration/v1alpha1",
|
||||
"admissionregistration/v1beta1",
|
||||
"apps/v1",
|
||||
|
|
@ -848,6 +849,8 @@
|
|||
"google.golang.org/grpc/codes",
|
||||
"google.golang.org/grpc/metadata",
|
||||
"google.golang.org/grpc/status",
|
||||
"k8s.io/api/admission/v1beta1",
|
||||
"k8s.io/api/admissionregistration/v1beta1",
|
||||
"k8s.io/api/apps/v1",
|
||||
"k8s.io/api/apps/v1beta2",
|
||||
"k8s.io/api/authorization/v1beta1",
|
||||
|
|
@ -861,12 +864,14 @@
|
|||
"k8s.io/apimachinery/pkg/labels",
|
||||
"k8s.io/apimachinery/pkg/runtime",
|
||||
"k8s.io/apimachinery/pkg/runtime/schema",
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer",
|
||||
"k8s.io/apimachinery/pkg/util/intstr",
|
||||
"k8s.io/apimachinery/pkg/util/runtime",
|
||||
"k8s.io/apimachinery/pkg/util/wait",
|
||||
"k8s.io/apimachinery/pkg/util/yaml",
|
||||
"k8s.io/apimachinery/pkg/version",
|
||||
"k8s.io/client-go/informers",
|
||||
"k8s.io/client-go/informers/admissionregistration/v1beta1",
|
||||
"k8s.io/client-go/informers/apps/v1beta2",
|
||||
"k8s.io/client-go/informers/core/v1",
|
||||
"k8s.io/client-go/kubernetes",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
## compile binaries
|
||||
FROM gcr.io/linkerd-io/go-deps:64a32a2a as golang
|
||||
FROM gcr.io/linkerd-io/go-deps:0c590d0e as golang
|
||||
WORKDIR /go/src/github.com/linkerd/linkerd2
|
||||
COPY cli cli
|
||||
COPY controller/k8s controller/k8s
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/linkerd/linkerd2/pkg/healthcheck"
|
||||
"github.com/linkerd/linkerd2/pkg/k8s"
|
||||
"github.com/spf13/cobra"
|
||||
appsV1 "k8s.io/api/apps/v1"
|
||||
|
|
@ -42,10 +43,6 @@ const (
|
|||
)
|
||||
|
||||
type injectOptions struct {
|
||||
inboundPort uint
|
||||
outboundPort uint
|
||||
ignoreInboundPorts []uint
|
||||
ignoreOutboundPorts []uint
|
||||
*proxyConfigOptions
|
||||
}
|
||||
|
||||
|
|
@ -64,11 +61,7 @@ type objMeta struct {
|
|||
|
||||
func newInjectOptions() *injectOptions {
|
||||
return &injectOptions{
|
||||
inboundPort: 4143,
|
||||
outboundPort: 4140,
|
||||
ignoreInboundPorts: nil,
|
||||
ignoreOutboundPorts: nil,
|
||||
proxyConfigOptions: newProxyConfigOptions(),
|
||||
proxyConfigOptions: newProxyConfigOptions(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,10 +100,6 @@ sub-folder. e.g. linkerd inject <folder> | kubectl apply -f -
|
|||
}
|
||||
|
||||
addProxyConfigFlags(cmd, options.proxyConfigOptions)
|
||||
cmd.PersistentFlags().UintVar(&options.inboundPort, "inbound-port", options.inboundPort, "Proxy port to use for inbound traffic")
|
||||
cmd.PersistentFlags().UintVar(&options.outboundPort, "outbound-port", options.outboundPort, "Proxy port to use for outbound traffic")
|
||||
cmd.PersistentFlags().UintSliceVar(&options.ignoreInboundPorts, "skip-inbound-ports", options.ignoreInboundPorts, "Ports that should skip the proxy and send directly to the application")
|
||||
cmd.PersistentFlags().UintSliceVar(&options.ignoreOutboundPorts, "skip-outbound-ports", options.ignoreOutboundPorts, "Outbound ports that should skip the proxy")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +171,7 @@ func injectObjectMeta(t *metaV1.ObjectMeta, k8sLabels map[string]string, options
|
|||
*/
|
||||
func injectPodSpec(t *v1.PodSpec, identity k8s.TLSIdentity, controlPlaneDNSNameOverride string, options *injectOptions, report *injectReport) bool {
|
||||
report.hostNetwork = t.HostNetwork
|
||||
report.sidecar = checkSidecars(t)
|
||||
report.sidecar = healthcheck.HasExistingSidecars(t)
|
||||
report.udp = checkUDPPorts(t)
|
||||
|
||||
// Skip injection if:
|
||||
|
|
@ -751,31 +740,3 @@ func checkUDPPorts(t *v1.PodSpec) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkSidecars(t *v1.PodSpec) bool {
|
||||
// check for known proxies and initContainers
|
||||
for _, container := range t.Containers {
|
||||
if strings.HasPrefix(container.Image, "gcr.io/linkerd-io/proxy:") ||
|
||||
strings.HasPrefix(container.Image, "gcr.io/istio-release/proxyv2:") ||
|
||||
strings.HasPrefix(container.Image, "gcr.io/heptio-images/contour:") ||
|
||||
strings.HasPrefix(container.Image, "docker.io/envoyproxy/envoy-alpine:") ||
|
||||
container.Name == "linkerd-proxy" ||
|
||||
container.Name == "istio-proxy" ||
|
||||
container.Name == "contour" ||
|
||||
container.Name == "envoy" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, ic := range t.InitContainers {
|
||||
if strings.HasPrefix(ic.Image, "gcr.io/linkerd-io/proxy-init:") ||
|
||||
strings.HasPrefix(ic.Image, "gcr.io/istio-release/proxy_init:") ||
|
||||
strings.HasPrefix(ic.Image, "gcr.io/heptio-images/contour:") ||
|
||||
ic.Name == "linkerd-init" ||
|
||||
ic.Name == "istio-init" ||
|
||||
ic.Name == "envoy-initconfig" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/linkerd/linkerd2/cli/install"
|
||||
|
|
@ -16,24 +17,47 @@ import (
|
|||
)
|
||||
|
||||
type installConfig struct {
|
||||
Namespace string
|
||||
ControllerImage string
|
||||
WebImage string
|
||||
PrometheusImage string
|
||||
GrafanaImage string
|
||||
ControllerReplicas uint
|
||||
WebReplicas uint
|
||||
PrometheusReplicas uint
|
||||
ImagePullPolicy string
|
||||
UUID string
|
||||
CliVersion string
|
||||
ControllerLogLevel string
|
||||
ControllerComponentLabel string
|
||||
CreatedByAnnotation string
|
||||
ProxyAPIPort uint
|
||||
EnableTLS bool
|
||||
TLSTrustAnchorConfigMapName string
|
||||
ProxyContainerName string
|
||||
Namespace string
|
||||
ControllerImage string
|
||||
WebImage string
|
||||
PrometheusImage string
|
||||
GrafanaImage string
|
||||
ControllerReplicas uint
|
||||
WebReplicas uint
|
||||
PrometheusReplicas uint
|
||||
ImagePullPolicy string
|
||||
UUID string
|
||||
CliVersion string
|
||||
ControllerLogLevel string
|
||||
ControllerComponentLabel string
|
||||
CreatedByAnnotation string
|
||||
ProxyAPIPort uint
|
||||
EnableTLS bool
|
||||
TLSTrustAnchorConfigMapName string
|
||||
ProxyContainerName string
|
||||
TLSTrustAnchorFileName string
|
||||
TLSCertFileName string
|
||||
TLSPrivateKeyFileName string
|
||||
TLSTrustAnchorVolumeSpecFileName string
|
||||
TLSIdentityVolumeSpecFileName string
|
||||
InboundPort uint
|
||||
OutboundPort uint
|
||||
IgnoreInboundPorts string
|
||||
IgnoreOutboundPorts string
|
||||
ProxyAutoInjectEnabled bool
|
||||
ProxyAutoInjectLabel string
|
||||
ProxyUID int64
|
||||
ProxyMetricsPort uint
|
||||
ProxyControlPort uint
|
||||
ProxyInjectorTLSSecret string
|
||||
ProxyInjectorSidecarConfig string
|
||||
ProxySpecFileName string
|
||||
ProxyInitSpecFileName string
|
||||
ProxyInitImage string
|
||||
ProxyImage string
|
||||
ProxyResourceRequestCPU string
|
||||
ProxyResourceRequestMemory string
|
||||
ProxyBindTimeout string
|
||||
}
|
||||
|
||||
type installOptions struct {
|
||||
|
|
@ -41,6 +65,7 @@ type installOptions struct {
|
|||
webReplicas uint
|
||||
prometheusReplicas uint
|
||||
controllerLogLevel string
|
||||
proxyAutoInject bool
|
||||
*proxyConfigOptions
|
||||
}
|
||||
|
||||
|
|
@ -52,6 +77,7 @@ func newInstallOptions() *installOptions {
|
|||
webReplicas: 1,
|
||||
prometheusReplicas: 1,
|
||||
controllerLogLevel: "info",
|
||||
proxyAutoInject: false,
|
||||
proxyConfigOptions: newProxyConfigOptions(),
|
||||
}
|
||||
}
|
||||
|
|
@ -78,6 +104,7 @@ func newCmdInstall() *cobra.Command {
|
|||
cmd.PersistentFlags().UintVar(&options.webReplicas, "web-replicas", options.webReplicas, "Replicas of the web server to deploy")
|
||||
cmd.PersistentFlags().UintVar(&options.prometheusReplicas, "prometheus-replicas", options.prometheusReplicas, "Replicas of prometheus to deploy")
|
||||
cmd.PersistentFlags().StringVar(&options.controllerLogLevel, "controller-log-level", options.controllerLogLevel, "Log level for the controller and web components")
|
||||
cmd.PersistentFlags().BoolVar(&options.proxyAutoInject, "proxy-auto-inject", options.proxyAutoInject, "Enable proxy sidecar auto-injection webhook; default to false")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
@ -86,25 +113,61 @@ func validateAndBuildConfig(options *installOptions) (*installConfig, error) {
|
|||
if err := validate(options); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ignoreInboundPorts := []string{
|
||||
fmt.Sprintf("%d", options.proxyControlPort),
|
||||
fmt.Sprintf("%d", options.proxyMetricsPort),
|
||||
}
|
||||
for _, p := range options.ignoreInboundPorts {
|
||||
ignoreInboundPorts = append(ignoreInboundPorts, fmt.Sprintf("%d", p))
|
||||
}
|
||||
ignoreOutboundPorts := []string{}
|
||||
for _, p := range options.ignoreOutboundPorts {
|
||||
ignoreOutboundPorts = append(ignoreOutboundPorts, fmt.Sprintf("%d", p))
|
||||
}
|
||||
|
||||
return &installConfig{
|
||||
Namespace: controlPlaneNamespace,
|
||||
ControllerImage: fmt.Sprintf("%s/controller:%s", options.dockerRegistry, options.linkerdVersion),
|
||||
WebImage: fmt.Sprintf("%s/web:%s", options.dockerRegistry, options.linkerdVersion),
|
||||
PrometheusImage: "prom/prometheus:v2.4.0",
|
||||
GrafanaImage: fmt.Sprintf("%s/grafana:%s", options.dockerRegistry, options.linkerdVersion),
|
||||
ControllerReplicas: options.controllerReplicas,
|
||||
WebReplicas: options.webReplicas,
|
||||
PrometheusReplicas: options.prometheusReplicas,
|
||||
ImagePullPolicy: options.imagePullPolicy,
|
||||
UUID: uuid.NewV4().String(),
|
||||
CliVersion: k8s.CreatedByAnnotationValue(),
|
||||
ControllerLogLevel: options.controllerLogLevel,
|
||||
ControllerComponentLabel: k8s.ControllerComponentLabel,
|
||||
CreatedByAnnotation: k8s.CreatedByAnnotation,
|
||||
ProxyAPIPort: options.proxyAPIPort,
|
||||
EnableTLS: options.enableTLS(),
|
||||
TLSTrustAnchorConfigMapName: k8s.TLSTrustAnchorConfigMapName,
|
||||
ProxyContainerName: k8s.ProxyContainerName,
|
||||
Namespace: controlPlaneNamespace,
|
||||
ControllerImage: fmt.Sprintf("%s/controller:%s", options.dockerRegistry, options.linkerdVersion),
|
||||
WebImage: fmt.Sprintf("%s/web:%s", options.dockerRegistry, options.linkerdVersion),
|
||||
PrometheusImage: "prom/prometheus:v2.4.0",
|
||||
GrafanaImage: fmt.Sprintf("%s/grafana:%s", options.dockerRegistry, options.linkerdVersion),
|
||||
ControllerReplicas: options.controllerReplicas,
|
||||
WebReplicas: options.webReplicas,
|
||||
PrometheusReplicas: options.prometheusReplicas,
|
||||
ImagePullPolicy: options.imagePullPolicy,
|
||||
UUID: uuid.NewV4().String(),
|
||||
CliVersion: k8s.CreatedByAnnotationValue(),
|
||||
ControllerLogLevel: options.controllerLogLevel,
|
||||
ControllerComponentLabel: k8s.ControllerComponentLabel,
|
||||
CreatedByAnnotation: k8s.CreatedByAnnotation,
|
||||
ProxyAPIPort: options.proxyAPIPort,
|
||||
EnableTLS: options.enableTLS(),
|
||||
TLSTrustAnchorConfigMapName: k8s.TLSTrustAnchorConfigMapName,
|
||||
ProxyContainerName: k8s.ProxyContainerName,
|
||||
TLSTrustAnchorFileName: k8s.TLSTrustAnchorFileName,
|
||||
TLSCertFileName: k8s.TLSCertFileName,
|
||||
TLSPrivateKeyFileName: k8s.TLSPrivateKeyFileName,
|
||||
TLSTrustAnchorVolumeSpecFileName: k8s.TLSTrustAnchorVolumeSpecFileName,
|
||||
TLSIdentityVolumeSpecFileName: k8s.TLSIdentityVolumeSpecFileName,
|
||||
InboundPort: options.inboundPort,
|
||||
OutboundPort: options.outboundPort,
|
||||
IgnoreInboundPorts: strings.Join(ignoreInboundPorts, ","),
|
||||
IgnoreOutboundPorts: strings.Join(ignoreOutboundPorts, ","),
|
||||
ProxyAutoInjectEnabled: options.proxyAutoInject,
|
||||
ProxyAutoInjectLabel: k8s.ProxyAutoInjectLabel,
|
||||
ProxyUID: options.proxyUID,
|
||||
ProxyMetricsPort: options.proxyMetricsPort,
|
||||
ProxyControlPort: options.proxyControlPort,
|
||||
ProxyInjectorTLSSecret: k8s.ProxyInjectorTLSSecret,
|
||||
ProxyInjectorSidecarConfig: k8s.ProxyInjectorSidecarConfig,
|
||||
ProxySpecFileName: k8s.ProxySpecFileName,
|
||||
ProxyInitSpecFileName: k8s.ProxyInitSpecFileName,
|
||||
ProxyInitImage: options.taggedProxyInitImage(),
|
||||
ProxyImage: options.taggedProxyImage(),
|
||||
ProxyResourceRequestCPU: options.proxyCpuRequest,
|
||||
ProxyResourceRequestMemory: options.proxyMemoryRequest,
|
||||
ProxyBindTimeout: "1m",
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +190,19 @@ func render(config installConfig, w io.Writer, options *installOptions) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.ProxyAutoInjectEnabled {
|
||||
proxyInjectorTemplate, err := template.New("linkerd").Parse(install.ProxyInjectorTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = proxyInjectorTemplate.Execute(buf, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
injectOptions := newInjectOptions()
|
||||
injectOptions.proxyConfigOptions = options.proxyConfigOptions
|
||||
|
||||
|
|
|
|||
|
|
@ -21,24 +21,47 @@ func TestRender(t *testing.T) {
|
|||
// A configuration that shows that all config setting strings are honored
|
||||
// by `render()`.
|
||||
metaConfig := installConfig{
|
||||
Namespace: "Namespace",
|
||||
ControllerImage: "ControllerImage",
|
||||
WebImage: "WebImage",
|
||||
PrometheusImage: "PrometheusImage",
|
||||
GrafanaImage: "GrafanaImage",
|
||||
ControllerReplicas: 1,
|
||||
WebReplicas: 2,
|
||||
PrometheusReplicas: 3,
|
||||
ImagePullPolicy: "ImagePullPolicy",
|
||||
UUID: "UUID",
|
||||
CliVersion: "CliVersion",
|
||||
ControllerLogLevel: "ControllerLogLevel",
|
||||
ControllerComponentLabel: "ControllerComponentLabel",
|
||||
CreatedByAnnotation: "CreatedByAnnotation",
|
||||
ProxyAPIPort: 123,
|
||||
EnableTLS: true,
|
||||
TLSTrustAnchorConfigMapName: "TLSTrustAnchorConfigMapName",
|
||||
ProxyContainerName: "ProxyContainerName",
|
||||
Namespace: "Namespace",
|
||||
ControllerImage: "ControllerImage",
|
||||
WebImage: "WebImage",
|
||||
PrometheusImage: "PrometheusImage",
|
||||
GrafanaImage: "GrafanaImage",
|
||||
ControllerReplicas: 1,
|
||||
WebReplicas: 2,
|
||||
PrometheusReplicas: 3,
|
||||
ImagePullPolicy: "ImagePullPolicy",
|
||||
UUID: "UUID",
|
||||
CliVersion: "CliVersion",
|
||||
ControllerLogLevel: "ControllerLogLevel",
|
||||
ControllerComponentLabel: "ControllerComponentLabel",
|
||||
CreatedByAnnotation: "CreatedByAnnotation",
|
||||
ProxyAPIPort: 123,
|
||||
EnableTLS: true,
|
||||
TLSTrustAnchorConfigMapName: "TLSTrustAnchorConfigMapName",
|
||||
ProxyContainerName: "ProxyContainerName",
|
||||
TLSTrustAnchorFileName: "TLSTrustAnchorFileName",
|
||||
TLSCertFileName: "TLSCertFileName",
|
||||
TLSPrivateKeyFileName: "TLSPrivateKeyFileName",
|
||||
TLSTrustAnchorVolumeSpecFileName: "TLSTrustAnchorVolumeSpecFileName",
|
||||
TLSIdentityVolumeSpecFileName: "TLSIdentityVolumeSpecFileName",
|
||||
ProxyAutoInjectEnabled: true,
|
||||
ProxyAutoInjectLabel: "ProxyAutoInjectLabel",
|
||||
ProxyUID: 2102,
|
||||
InboundPort: 4143,
|
||||
OutboundPort: 4140,
|
||||
ProxyControlPort: 4190,
|
||||
ProxyMetricsPort: 4191,
|
||||
ProxyInitImage: "ProxyInitImage",
|
||||
ProxyImage: "ProxyImage",
|
||||
ProxyInjectorTLSSecret: "ProxyInjectorTLSSecret",
|
||||
ProxyInjectorSidecarConfig: "ProxyInjectorSidecarConfig",
|
||||
ProxySpecFileName: "ProxySpecFileName",
|
||||
ProxyInitSpecFileName: "ProxyInitSpecFileName",
|
||||
IgnoreInboundPorts: "4190,4191,1,2,3",
|
||||
IgnoreOutboundPorts: "2,3,4",
|
||||
ProxyResourceRequestCPU: "RequestCPU",
|
||||
ProxyResourceRequestMemory: "RequestMemory",
|
||||
ProxyBindTimeout: "1m",
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
|
|
|
|||
|
|
@ -135,6 +135,10 @@ type proxyConfigOptions struct {
|
|||
initImage string
|
||||
dockerRegistry string
|
||||
imagePullPolicy string
|
||||
inboundPort uint
|
||||
outboundPort uint
|
||||
ignoreInboundPorts []uint
|
||||
ignoreOutboundPorts []uint
|
||||
proxyUID int64
|
||||
proxyLogLevel string
|
||||
proxyBindTimeout string
|
||||
|
|
@ -159,6 +163,10 @@ func newProxyConfigOptions() *proxyConfigOptions {
|
|||
initImage: defaultDockerRegistry + "/proxy-init",
|
||||
dockerRegistry: defaultDockerRegistry,
|
||||
imagePullPolicy: "IfNotPresent",
|
||||
inboundPort: 4143,
|
||||
outboundPort: 4140,
|
||||
ignoreInboundPorts: nil,
|
||||
ignoreOutboundPorts: nil,
|
||||
proxyUID: 2102,
|
||||
proxyLogLevel: "warn,linkerd2_proxy=info",
|
||||
proxyBindTimeout: "10s",
|
||||
|
|
@ -231,11 +239,14 @@ func addProxyConfigFlags(cmd *cobra.Command, options *proxyConfigOptions) {
|
|||
cmd.PersistentFlags().Int64Var(&options.proxyUID, "proxy-uid", options.proxyUID, "Run the proxy under this user ID")
|
||||
cmd.PersistentFlags().StringVar(&options.proxyLogLevel, "proxy-log-level", options.proxyLogLevel, "Log level for the proxy")
|
||||
cmd.PersistentFlags().StringVar(&options.proxyBindTimeout, "proxy-bind-timeout", options.proxyBindTimeout, "Timeout the proxy will use")
|
||||
cmd.PersistentFlags().UintVar(&options.inboundPort, "inbound-port", options.inboundPort, "Proxy port to use for inbound traffic")
|
||||
cmd.PersistentFlags().UintVar(&options.outboundPort, "outbound-port", options.outboundPort, "Proxy port to use for outbound traffic")
|
||||
cmd.PersistentFlags().UintVar(&options.proxyAPIPort, "api-port", options.proxyAPIPort, "Port where the Linkerd controller is running")
|
||||
cmd.PersistentFlags().UintVar(&options.proxyControlPort, "control-port", options.proxyControlPort, "Proxy port to use for control")
|
||||
cmd.PersistentFlags().UintVar(&options.proxyMetricsPort, "metrics-port", options.proxyMetricsPort, "Proxy port to serve metrics on")
|
||||
cmd.PersistentFlags().StringVar(&options.tls, "tls", options.tls, "Enable TLS; valid settings: \"optional\"")
|
||||
|
||||
cmd.PersistentFlags().StringVar(&options.proxyCpuRequest, "proxy-cpu", options.proxyCpuRequest, "Amount of CPU units that the proxy sidecar requests")
|
||||
cmd.PersistentFlags().StringVar(&options.proxyMemoryRequest, "proxy-memory", options.proxyMemoryRequest, "Amount of Memory that the proxy sidecar requests")
|
||||
cmd.PersistentFlags().UintSliceVar(&options.ignoreInboundPorts, "skip-inbound-ports", options.ignoreInboundPorts, "Ports that should skip the proxy and send directly to the application")
|
||||
cmd.PersistentFlags().UintSliceVar(&options.ignoreOutboundPorts, "skip-outbound-ports", options.ignoreOutboundPorts, "Outbound ports that should skip the proxy")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ kind: Namespace
|
|||
apiVersion: v1
|
||||
metadata:
|
||||
name: Namespace
|
||||
labels:
|
||||
ProxyAutoInjectLabel: disabled
|
||||
|
||||
### Service Account Controller ###
|
||||
---
|
||||
|
|
@ -887,6 +889,9 @@ rules:
|
|||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
verbs: ["create", "update"]
|
||||
- apiGroups: ["admissionregistration.k8s.io"]
|
||||
resources: ["mutatingwebhookconfigurations"]
|
||||
verbs: ["list", "get", "watch"]
|
||||
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
|
|
@ -933,6 +938,7 @@ spec:
|
|||
- args:
|
||||
- ca
|
||||
- -controller-namespace=Namespace
|
||||
- -proxy-auto-inject=true
|
||||
- -log-level=ControllerLogLevel
|
||||
image: ControllerImage
|
||||
imagePullPolicy: ImagePullPolicy
|
||||
|
|
@ -1015,3 +1021,295 @@ spec:
|
|||
serviceAccount: linkerd-ca
|
||||
status: {}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
CreatedByAnnotation: CliVersion
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
ControllerComponentLabel: proxy-injector
|
||||
name: proxy-injector
|
||||
namespace: Namespace
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
ControllerComponentLabel: proxy-injector
|
||||
strategy: {}
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
CreatedByAnnotation: CliVersion
|
||||
linkerd.io/created-by: linkerd/cli undefined
|
||||
linkerd.io/proxy-version: undefined
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
ControllerComponentLabel: proxy-injector
|
||||
linkerd.io/control-plane-ns: Namespace
|
||||
linkerd.io/proxy-deployment: proxy-injector
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- proxy-injector
|
||||
- -controller-namespace=Namespace
|
||||
- -log-level=ControllerLogLevel
|
||||
image: ControllerImage
|
||||
imagePullPolicy: ImagePullPolicy
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /ping
|
||||
port: 9995
|
||||
initialDelaySeconds: 10
|
||||
name: proxy-injector
|
||||
ports:
|
||||
- containerPort: 443
|
||||
name: proxy-injector
|
||||
readinessProbe:
|
||||
failureThreshold: 7
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 9995
|
||||
resources: {}
|
||||
volumeMounts:
|
||||
- mountPath: /var/linkerd-io/trust-anchors
|
||||
name: linkerd-trust-anchors
|
||||
readOnly: true
|
||||
- mountPath: /var/linkerd-io/identity
|
||||
name: webhook-secrets
|
||||
readOnly: true
|
||||
- mountPath: /var/linkerd-io/config
|
||||
name: proxy-spec
|
||||
- env:
|
||||
- name: LINKERD2_PROXY_LOG
|
||||
value: warn,linkerd2_proxy=info
|
||||
- name: LINKERD2_PROXY_BIND_TIMEOUT
|
||||
value: 10s
|
||||
- name: LINKERD2_PROXY_CONTROL_URL
|
||||
value: tcp://proxy-api.Namespace.svc.cluster.local:8086
|
||||
- name: LINKERD2_PROXY_CONTROL_LISTENER
|
||||
value: tcp://0.0.0.0:4190
|
||||
- name: LINKERD2_PROXY_METRICS_LISTENER
|
||||
value: tcp://0.0.0.0:4191
|
||||
- name: LINKERD2_PROXY_OUTBOUND_LISTENER
|
||||
value: tcp://127.0.0.1:4140
|
||||
- name: LINKERD2_PROXY_INBOUND_LISTENER
|
||||
value: tcp://0.0.0.0:4143
|
||||
- name: LINKERD2_PROXY_POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
image: gcr.io/linkerd-io/proxy:undefined
|
||||
imagePullPolicy: IfNotPresent
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: 4191
|
||||
initialDelaySeconds: 10
|
||||
name: linkerd-proxy
|
||||
ports:
|
||||
- containerPort: 4143
|
||||
name: linkerd-proxy
|
||||
- containerPort: 4191
|
||||
name: linkerd-metrics
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: 4191
|
||||
initialDelaySeconds: 10
|
||||
resources: {}
|
||||
securityContext:
|
||||
runAsUser: 2102
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
initContainers:
|
||||
- args:
|
||||
- --incoming-proxy-port
|
||||
- "4143"
|
||||
- --outgoing-proxy-port
|
||||
- "4140"
|
||||
- --proxy-uid
|
||||
- "2102"
|
||||
- --inbound-ports-to-ignore
|
||||
- 4190,4191
|
||||
image: gcr.io/linkerd-io/proxy-init:undefined
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: linkerd-init
|
||||
resources: {}
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
privileged: false
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
serviceAccount: linkerd-proxy-injector
|
||||
volumes:
|
||||
- name: webhook-secrets
|
||||
secret:
|
||||
optional: true
|
||||
secretName: ProxyInjectorTLSSecret
|
||||
- configMap:
|
||||
name: ProxyInjectorSidecarConfig
|
||||
name: proxy-spec
|
||||
status: {}
|
||||
---
|
||||
### Proxy Injector Service Account ###
|
||||
kind: ServiceAccount
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: linkerd-proxy-injector
|
||||
namespace: Namespace
|
||||
|
||||
---
|
||||
### Proxy Injector RBAC ###
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: linkerd-Namespace-proxy-injector
|
||||
rules:
|
||||
- apiGroups: ["admissionregistration.k8s.io"]
|
||||
resources: ["mutatingwebhookconfigurations"]
|
||||
verbs: ["create", "update", "get", "watch"]
|
||||
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: linkerd-Namespace-proxy-injector
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: linkerd-proxy-injector
|
||||
namespace: Namespace
|
||||
apiGroup: ""
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: linkerd-Namespace-proxy-injector
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
---
|
||||
### Proxy Injector Service ###
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: proxy-injector
|
||||
namespace: Namespace
|
||||
labels:
|
||||
ControllerComponentLabel: proxy-injector
|
||||
annotations:
|
||||
CreatedByAnnotation: CliVersion
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
ControllerComponentLabel: proxy-injector
|
||||
ports:
|
||||
- name: proxy-injector
|
||||
port: 443
|
||||
targetPort: proxy-injector
|
||||
|
||||
---
|
||||
### Proxy Sidecar Container Spec ###
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: ProxyInjectorSidecarConfig
|
||||
namespace: Namespace
|
||||
labels:
|
||||
ControllerComponentLabel: proxy-injector
|
||||
annotations:
|
||||
CreatedByAnnotation: CliVersion
|
||||
data:
|
||||
ProxyInitSpecFileName: |
|
||||
args:
|
||||
- --incoming-proxy-port
|
||||
- 4143
|
||||
- --outgoing-proxy-port
|
||||
- 4140
|
||||
- --proxy-uid
|
||||
- 2102
|
||||
- --inbound-ports-to-ignore
|
||||
- 4190,4191,1,2,3
|
||||
- --outbound-ports-to-ignore
|
||||
- 2,3,4
|
||||
image: ProxyInitImage
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: linkerd-init
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
privileged: false
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
ProxySpecFileName: |
|
||||
env:
|
||||
- name: LINKERD2_PROXY_LOG
|
||||
value: warn,linkerd2_proxy=info
|
||||
- name: LINKERD2_PROXY_BIND_TIMEOUT
|
||||
value: 1m
|
||||
- name: LINKERD2_PROXY_CONTROL_URL
|
||||
value: tcp://proxy-api.Namespace.svc.cluster.local:123
|
||||
- name: LINKERD2_PROXY_CONTROL_LISTENER
|
||||
value: tcp://0.0.0.0:4190
|
||||
- name: LINKERD2_PROXY_METRICS_LISTENER
|
||||
value: tcp://0.0.0.0:4191
|
||||
- name: LINKERD2_PROXY_OUTBOUND_LISTENER
|
||||
value: tcp://127.0.0.1:4140
|
||||
- name: LINKERD2_PROXY_INBOUND_LISTENER
|
||||
value: tcp://0.0.0.0:4143
|
||||
- name: LINKERD2_PROXY_POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: LINKERD2_PROXY_TLS_TRUST_ANCHORS
|
||||
value: /var/linkerd-io/trust-anchors/TLSTrustAnchorFileName
|
||||
- name: LINKERD2_PROXY_TLS_CERT
|
||||
value: /var/linkerd-io/identity/TLSCertFileName
|
||||
- name: LINKERD2_PROXY_TLS_PRIVATE_KEY
|
||||
value: /var/linkerd-io/identity/TLSPrivateKeyFileName
|
||||
- name: LINKERD2_PROXY_TLS_POD_IDENTITY
|
||||
value: "" # this value will be computed by the webhook
|
||||
- name: LINKERD2_PROXY_CONTROLLER_NAMESPACE
|
||||
value: Namespace
|
||||
- name: LINKERD2_PROXY_TLS_CONTROLLER_IDENTITY
|
||||
value: "" # this value will be computed by the webhook
|
||||
image: ProxyImage
|
||||
imagePullPolicy: IfNotPresent
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: 4191
|
||||
initialDelaySeconds: 10
|
||||
name: linkerd-proxy
|
||||
ports:
|
||||
- containerPort: 4143
|
||||
name: linkerd-proxy
|
||||
- containerPort: 4191
|
||||
name: linkerd-metrics
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: 4191
|
||||
initialDelaySeconds: 10
|
||||
resources:
|
||||
requests:
|
||||
cpu: RequestCPU
|
||||
memory: RequestMemory
|
||||
securityContext:
|
||||
runAsUser: 2102
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
volumeMounts:
|
||||
- mountPath: /var/linkerd-io/trust-anchors
|
||||
name: linkerd-trust-anchors
|
||||
readOnly: true
|
||||
- mountPath: /var/linkerd-io/identity
|
||||
name: linkerd-secrets
|
||||
readOnly: true
|
||||
TLSTrustAnchorVolumeSpecFileName: |
|
||||
name: linkerd-trust-anchors
|
||||
configMap:
|
||||
name: TLSTrustAnchorConfigMapName
|
||||
optional: true
|
||||
TLSIdentityVolumeSpecFileName: |
|
||||
name: linkerd-secrets
|
||||
secret:
|
||||
secretName: "" # this value will be computed by the webhook
|
||||
optional: true
|
||||
---
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ kind: Namespace
|
|||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{.Namespace}}
|
||||
{{- if and .EnableTLS .ProxyAutoInjectEnabled }}
|
||||
labels:
|
||||
{{.ProxyAutoInjectLabel}}: disabled
|
||||
{{- end }}
|
||||
|
||||
### Service Account Controller ###
|
||||
---
|
||||
|
|
@ -613,6 +617,11 @@ rules:
|
|||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
verbs: ["create", "update"]
|
||||
{{- if and .EnableTLS .ProxyAutoInjectEnabled }}
|
||||
- apiGroups: ["admissionregistration.k8s.io"]
|
||||
resources: ["mutatingwebhookconfigurations"]
|
||||
verbs: ["list", "get", "watch"]
|
||||
{{- end }}
|
||||
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
|
|
@ -659,6 +668,9 @@ spec:
|
|||
args:
|
||||
- "ca"
|
||||
- "-controller-namespace={{.Namespace}}"
|
||||
{{- if and .EnableTLS .ProxyAutoInjectEnabled }}
|
||||
- "-proxy-auto-inject={{ .ProxyAutoInjectEnabled }}"
|
||||
{{- end }}
|
||||
- "-log-level={{.ControllerLogLevel}}"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
|
@ -671,3 +683,240 @@ spec:
|
|||
port: 9997
|
||||
failureThreshold: 7
|
||||
`
|
||||
|
||||
const ProxyInjectorTemplate = `
|
||||
---
|
||||
### Proxy Injector Deployment ###
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: proxy-injector
|
||||
namespace: {{.Namespace}}
|
||||
labels:
|
||||
{{.ControllerComponentLabel}}: proxy-injector
|
||||
annotations:
|
||||
{{.CreatedByAnnotation}}: {{.CliVersion}}
|
||||
spec:
|
||||
replicas: {{.ControllerReplicas}}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{.ControllerComponentLabel}}: proxy-injector
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{.ControllerComponentLabel}}: proxy-injector
|
||||
annotations:
|
||||
{{.CreatedByAnnotation}}: {{.CliVersion}}
|
||||
spec:
|
||||
serviceAccount: linkerd-proxy-injector
|
||||
containers:
|
||||
- name: proxy-injector
|
||||
image: {{.ControllerImage}}
|
||||
imagePullPolicy: {{.ImagePullPolicy}}
|
||||
args:
|
||||
- "proxy-injector"
|
||||
- "-controller-namespace={{.Namespace}}"
|
||||
- "-log-level={{.ControllerLogLevel}}"
|
||||
ports:
|
||||
- name: proxy-injector
|
||||
containerPort: 443
|
||||
volumeMounts:
|
||||
- name: linkerd-trust-anchors
|
||||
mountPath: /var/linkerd-io/trust-anchors
|
||||
readOnly: true
|
||||
- name: webhook-secrets
|
||||
mountPath: /var/linkerd-io/identity
|
||||
readOnly: true
|
||||
- name: proxy-spec
|
||||
mountPath: /var/linkerd-io/config
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /ping
|
||||
port: 9995
|
||||
initialDelaySeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 9995
|
||||
failureThreshold: 7
|
||||
volumes:
|
||||
- name: webhook-secrets
|
||||
secret:
|
||||
secretName: {{.ProxyInjectorTLSSecret}}
|
||||
optional: true
|
||||
- name: proxy-spec
|
||||
configMap:
|
||||
name: {{.ProxyInjectorSidecarConfig}}
|
||||
|
||||
---
|
||||
### Proxy Injector Service Account ###
|
||||
kind: ServiceAccount
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: linkerd-proxy-injector
|
||||
namespace: {{.Namespace}}
|
||||
|
||||
---
|
||||
### Proxy Injector RBAC ###
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: linkerd-{{.Namespace}}-proxy-injector
|
||||
rules:
|
||||
- apiGroups: ["admissionregistration.k8s.io"]
|
||||
resources: ["mutatingwebhookconfigurations"]
|
||||
verbs: ["create", "update", "get", "watch"]
|
||||
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: linkerd-{{.Namespace}}-proxy-injector
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: linkerd-proxy-injector
|
||||
namespace: {{.Namespace}}
|
||||
apiGroup: ""
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: linkerd-{{.Namespace}}-proxy-injector
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
---
|
||||
### Proxy Injector Service ###
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: proxy-injector
|
||||
namespace: {{.Namespace}}
|
||||
labels:
|
||||
{{.ControllerComponentLabel}}: proxy-injector
|
||||
annotations:
|
||||
{{.CreatedByAnnotation}}: {{.CliVersion}}
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
{{.ControllerComponentLabel}}: proxy-injector
|
||||
ports:
|
||||
- name: proxy-injector
|
||||
port: 443
|
||||
targetPort: proxy-injector
|
||||
|
||||
---
|
||||
### Proxy Sidecar Container Spec ###
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{.ProxyInjectorSidecarConfig}}
|
||||
namespace: {{.Namespace}}
|
||||
labels:
|
||||
{{.ControllerComponentLabel}}: proxy-injector
|
||||
annotations:
|
||||
{{.CreatedByAnnotation}}: {{.CliVersion}}
|
||||
data:
|
||||
{{.ProxyInitSpecFileName}}: |
|
||||
args:
|
||||
- --incoming-proxy-port
|
||||
- {{.InboundPort}}
|
||||
- --outgoing-proxy-port
|
||||
- {{.OutboundPort}}
|
||||
- --proxy-uid
|
||||
- {{.ProxyUID}}
|
||||
{{- if ne (len .IgnoreInboundPorts) 0}}
|
||||
- --inbound-ports-to-ignore
|
||||
- {{.IgnoreInboundPorts}}
|
||||
{{- end }}
|
||||
{{- if ne (len .IgnoreOutboundPorts) 0}}
|
||||
- --outbound-ports-to-ignore
|
||||
- {{.IgnoreOutboundPorts}}
|
||||
{{- end}}
|
||||
image: {{.ProxyInitImage}}
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: linkerd-init
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
privileged: false
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
{{.ProxySpecFileName}}: |
|
||||
env:
|
||||
- name: LINKERD2_PROXY_LOG
|
||||
value: warn,linkerd2_proxy=info
|
||||
- name: LINKERD2_PROXY_BIND_TIMEOUT
|
||||
value: {{.ProxyBindTimeout}}
|
||||
- name: LINKERD2_PROXY_CONTROL_URL
|
||||
value: tcp://proxy-api.{{.Namespace}}.svc.cluster.local:{{.ProxyAPIPort}}
|
||||
- name: LINKERD2_PROXY_CONTROL_LISTENER
|
||||
value: tcp://0.0.0.0:{{.ProxyControlPort}}
|
||||
- name: LINKERD2_PROXY_METRICS_LISTENER
|
||||
value: tcp://0.0.0.0:{{.ProxyMetricsPort}}
|
||||
- name: LINKERD2_PROXY_OUTBOUND_LISTENER
|
||||
value: tcp://127.0.0.1:{{.OutboundPort}}
|
||||
- name: LINKERD2_PROXY_INBOUND_LISTENER
|
||||
value: tcp://0.0.0.0:{{.InboundPort}}
|
||||
- name: LINKERD2_PROXY_POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: LINKERD2_PROXY_TLS_TRUST_ANCHORS
|
||||
value: /var/linkerd-io/trust-anchors/{{.TLSTrustAnchorFileName}}
|
||||
- name: LINKERD2_PROXY_TLS_CERT
|
||||
value: /var/linkerd-io/identity/{{.TLSCertFileName}}
|
||||
- name: LINKERD2_PROXY_TLS_PRIVATE_KEY
|
||||
value: /var/linkerd-io/identity/{{.TLSPrivateKeyFileName}}
|
||||
- name: LINKERD2_PROXY_TLS_POD_IDENTITY
|
||||
value: "" # this value will be computed by the webhook
|
||||
- name: LINKERD2_PROXY_CONTROLLER_NAMESPACE
|
||||
value: {{.Namespace}}
|
||||
- name: LINKERD2_PROXY_TLS_CONTROLLER_IDENTITY
|
||||
value: "" # this value will be computed by the webhook
|
||||
image: {{.ProxyImage}}
|
||||
imagePullPolicy: IfNotPresent
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: {{.ProxyMetricsPort}}
|
||||
initialDelaySeconds: 10
|
||||
name: linkerd-proxy
|
||||
ports:
|
||||
- containerPort: {{.InboundPort}}
|
||||
name: linkerd-proxy
|
||||
- containerPort: {{.ProxyMetricsPort}}
|
||||
name: linkerd-metrics
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: {{.ProxyMetricsPort}}
|
||||
initialDelaySeconds: 10
|
||||
{{- if or .ProxyResourceRequestCPU .ProxyResourceRequestMemory }}
|
||||
resources:
|
||||
requests:
|
||||
{{- if .ProxyResourceRequestCPU }}
|
||||
cpu: {{.ProxyResourceRequestCPU}}
|
||||
{{- end }}
|
||||
{{- if .ProxyResourceRequestMemory}}
|
||||
memory: {{.ProxyResourceRequestMemory}}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
securityContext:
|
||||
runAsUser: {{.ProxyUID}}
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
volumeMounts:
|
||||
- mountPath: /var/linkerd-io/trust-anchors
|
||||
name: linkerd-trust-anchors
|
||||
readOnly: true
|
||||
- mountPath: /var/linkerd-io/identity
|
||||
name: linkerd-secrets
|
||||
readOnly: true
|
||||
{{.TLSTrustAnchorVolumeSpecFileName}}: |
|
||||
name: linkerd-trust-anchors
|
||||
configMap:
|
||||
name: {{.TLSTrustAnchorConfigMapName}}
|
||||
optional: true
|
||||
{{.TLSIdentityVolumeSpecFileName}}: |
|
||||
name: linkerd-secrets
|
||||
secret:
|
||||
secretName: "" # this value will be computed by the webhook
|
||||
optional: true
|
||||
`
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
## compile controller services
|
||||
FROM gcr.io/linkerd-io/go-deps:64a32a2a as golang
|
||||
FROM gcr.io/linkerd-io/go-deps:0c590d0e as golang
|
||||
WORKDIR /go/src/github.com/linkerd/linkerd2
|
||||
COPY controller/gen controller/gen
|
||||
COPY pkg pkg
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/linkerd/linkerd2/controller/k8s"
|
||||
pkgK8s "github.com/linkerd/linkerd2/pkg/k8s"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
@ -31,7 +32,7 @@ type CertificateController struct {
|
|||
queue workqueue.RateLimitingInterface
|
||||
}
|
||||
|
||||
func NewCertificateController(controllerNamespace string, k8sAPI *k8s.API) (*CertificateController, error) {
|
||||
func NewCertificateController(controllerNamespace string, k8sAPI *k8s.API, proxyAutoInject bool) (*CertificateController, error) {
|
||||
ca, err := NewCA()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -52,6 +53,15 @@ func NewCertificateController(controllerNamespace string, k8sAPI *k8s.API) (*Cer
|
|||
},
|
||||
)
|
||||
|
||||
if proxyAutoInject {
|
||||
k8sAPI.MWC().Informer().AddEventHandler(
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: c.handleMWCAdd,
|
||||
UpdateFunc: c.handleMWCUpdate,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
c.syncHandler = c.syncObject
|
||||
|
||||
return c, nil
|
||||
|
|
@ -134,6 +144,7 @@ func (c *CertificateController) syncSecret(key string) error {
|
|||
Namespace: parts[2],
|
||||
ControllerNamespace: c.namespace,
|
||||
}
|
||||
|
||||
dnsName := identity.ToDNSName()
|
||||
secretName := identity.ToSecretName()
|
||||
certAndPrivateKey, err := c.ca.IssueEndEntityCertificate(dnsName)
|
||||
|
|
@ -172,3 +183,17 @@ func (c *CertificateController) handlePodAdd(obj interface{}) {
|
|||
func (c *CertificateController) handlePodUpdate(oldObj, newObj interface{}) {
|
||||
c.handlePodAdd(newObj)
|
||||
}
|
||||
|
||||
func (c *CertificateController) handleMWCAdd(obj interface{}) {
|
||||
mwc := obj.(*v1beta1.MutatingWebhookConfiguration)
|
||||
log.Debugf("enqueuing secret write for mutating webhook configuration %q", mwc.ObjectMeta.Name)
|
||||
for _, webhook := range mwc.Webhooks {
|
||||
if mwc.Name == pkgK8s.ProxyInjectorWebhookConfig {
|
||||
c.queue.Add(fmt.Sprintf("%s.%s.%s", webhook.ClientConfig.Service.Name, pkgK8s.Service, webhook.ClientConfig.Service.Namespace))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CertificateController) handleMWCUpdate(oldObj, newObj interface{}) {
|
||||
c.handleMWCAdd(newObj)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ func new(fixtures ...string) (*CertificateController, chan bool, chan struct{},
|
|||
return nil, nil, nil, fmt.Errorf("NewFakeAPI returned an error: %s", err)
|
||||
}
|
||||
|
||||
controller, err := NewCertificateController(controllerNS, k8sAPI)
|
||||
controller, err := NewCertificateController(controllerNS, k8sAPI, false)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("NewCertificateController returned an error: %s", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ func main() {
|
|||
metricsAddr := flag.String("metrics-addr", ":9997", "address to serve scrapable metrics on")
|
||||
controllerNamespace := flag.String("controller-namespace", "linkerd", "namespace in which Linkerd is installed")
|
||||
kubeConfigPath := flag.String("kubeconfig", "", "path to kube config")
|
||||
proxyAutoInject := flag.Bool("proxy-auto-inject", false, "if true, watch for the add and update events of mutating webhook configurations")
|
||||
flags.ConfigureAndParse()
|
||||
|
||||
stop := make(chan os.Signal, 1)
|
||||
|
|
@ -26,13 +27,15 @@ func main() {
|
|||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
k8sAPI := k8s.NewAPI(
|
||||
k8sClient,
|
||||
k8s.Pod,
|
||||
k8s.RS,
|
||||
)
|
||||
|
||||
controller, err := ca.NewCertificateController(*controllerNamespace, k8sAPI)
|
||||
var k8sAPI *k8s.API
|
||||
if *proxyAutoInject {
|
||||
k8sAPI = k8s.NewAPI(k8sClient, k8s.Pod, k8s.RS, k8s.MWC)
|
||||
} else {
|
||||
k8sAPI = k8s.NewAPI(k8sClient, k8s.Pod, k8s.RS)
|
||||
}
|
||||
|
||||
controller, err := ca.NewCertificateController(*controllerNamespace, k8sAPI, *proxyAutoInject)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create CertificateController: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"github.com/linkerd/linkerd2/controller/k8s"
|
||||
"github.com/linkerd/linkerd2/controller/proxy-injector"
|
||||
"github.com/linkerd/linkerd2/pkg/admin"
|
||||
"github.com/linkerd/linkerd2/pkg/flags"
|
||||
k8sPkg "github.com/linkerd/linkerd2/pkg/k8s"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
func main() {
|
||||
metricsAddr := flag.String("metrics-addr", ":9995", "address to serve scrapable metrics on")
|
||||
addr := flag.String("addr", ":443", "address to serve on")
|
||||
kubeconfig := flag.String("kubeconfig", "", "path to kubeconfig")
|
||||
controllerNamespace := flag.String("controller-namespace", "linkerd", "namespace in which Linkerd is installed")
|
||||
volumeMountsWaitTime := flag.Duration("volume-mounts-wait", 3*time.Minute, "maximum wait time for the secret volumes to mount before the timeout expires")
|
||||
webhookServiceName := flag.String("webhook-service", "proxy-injector.linkerd.io", "name of the admission webhook")
|
||||
flags.ConfigureAndParse()
|
||||
|
||||
stop := make(chan os.Signal, 1)
|
||||
defer close(stop)
|
||||
signal.Notify(stop, os.Interrupt, os.Kill)
|
||||
|
||||
k8sClient, err := k8s.NewClientSet(*kubeconfig)
|
||||
if err != nil {
|
||||
log.Fatal("failed to initialize Kubernetes client: ", err)
|
||||
}
|
||||
|
||||
log.Infof("waiting for the trust anchors volume to mount at %s", k8sPkg.MountPathTLSTrustAnchor)
|
||||
if err := waitForMounts(*volumeMountsWaitTime, k8sPkg.MountPathTLSTrustAnchor); err != context.Canceled {
|
||||
log.Fatalf("failed to mount the ca bundle: %s", err)
|
||||
}
|
||||
|
||||
webhookConfig, err := injector.NewWebhookConfig(k8sClient, *controllerNamespace, *webhookServiceName, k8sPkg.MountPathTLSTrustAnchor)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to read the trust anchor file: %s", err)
|
||||
}
|
||||
|
||||
mwc, err := webhookConfig.CreateOrUpdate()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create the mutating webhook configurations resource: ", err)
|
||||
}
|
||||
log.Info("created or updated mutating webhook configuration: ", mwc.ObjectMeta.SelfLink)
|
||||
|
||||
var (
|
||||
certFile = k8sPkg.MountPathTLSIdentityCert
|
||||
keyFile = k8sPkg.MountPathTLSIdentityKey
|
||||
)
|
||||
log.Infof("waiting for the tls secrets to mount at %s and %s", certFile, keyFile)
|
||||
if err := waitForMounts(*volumeMountsWaitTime, certFile, keyFile); err != context.Canceled {
|
||||
log.Fatalf("failed to mount the tls secrets: %s", err)
|
||||
}
|
||||
|
||||
resources := &injector.WebhookResources{
|
||||
FileProxySpec: k8sPkg.MountPathConfigProxySpec,
|
||||
FileProxyInitSpec: k8sPkg.MountPathConfigProxyInitSpec,
|
||||
FileTLSTrustAnchorVolumeSpec: k8sPkg.MountPathTLSTrustAnchorVolumeSpec,
|
||||
FileTLSIdentityVolumeSpec: k8sPkg.MountPathTLSIdentityVolumeSpec,
|
||||
}
|
||||
s, err := injector.NewWebhookServer(k8sClient, resources, *addr, *controllerNamespace, certFile, keyFile)
|
||||
if err != nil {
|
||||
log.Fatal("failed to initialize the webhook server: ", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
log.Infof("listening at %s", *addr)
|
||||
if err := s.ListenAndServeTLS("", ""); err != nil {
|
||||
if err == http.ErrServerClosed {
|
||||
return
|
||||
}
|
||||
log.Fatal(err)
|
||||
}
|
||||
}()
|
||||
go admin.StartServer(*metricsAddr, nil)
|
||||
|
||||
<-stop
|
||||
log.Info("shutting down webhook server")
|
||||
if err := s.Shutdown(); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func waitForMounts(timeout time.Duration, paths ...string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
wait.Until(func() {
|
||||
ready := 0
|
||||
for _, file := range paths {
|
||||
if _, err := os.Stat(file); err != nil {
|
||||
log.Infof("mount not ready: %s", file)
|
||||
return
|
||||
}
|
||||
|
||||
ready += 1
|
||||
log.Infof("mount ready: %s", file)
|
||||
if ready == len(paths) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
cancel()
|
||||
}, time.Millisecond*500, ctx.Done())
|
||||
|
||||
return ctx.Err()
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/informers"
|
||||
arinformers "k8s.io/client-go/informers/admissionregistration/v1beta1"
|
||||
appinformers "k8s.io/client-go/informers/apps/v1beta2"
|
||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
|
@ -32,6 +33,7 @@ const (
|
|||
RC
|
||||
RS
|
||||
Svc
|
||||
MWC // mutating webhook configuration
|
||||
)
|
||||
|
||||
// API provides shared informers for all Kubernetes objects
|
||||
|
|
@ -46,6 +48,7 @@ type API struct {
|
|||
rc coreinformers.ReplicationControllerInformer
|
||||
rs appinformers.ReplicaSetInformer
|
||||
svc coreinformers.ServiceInformer
|
||||
mwc arinformers.MutatingWebhookConfigurationInformer
|
||||
|
||||
syncChecks []cache.InformerSynced
|
||||
sharedInformers informers.SharedInformerFactory
|
||||
|
|
@ -87,6 +90,9 @@ func NewAPI(k8sClient kubernetes.Interface, resources ...ApiResource) *API {
|
|||
case Svc:
|
||||
api.svc = sharedInformers.Core().V1().Services()
|
||||
api.syncChecks = append(api.syncChecks, api.svc.Informer().HasSynced)
|
||||
case MWC:
|
||||
api.mwc = sharedInformers.Admissionregistration().V1beta1().MutatingWebhookConfigurations()
|
||||
api.syncChecks = append(api.syncChecks, api.mwc.Informer().HasSynced)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,6 +175,13 @@ func (api *API) CM() coreinformers.ConfigMapInformer {
|
|||
return api.cm
|
||||
}
|
||||
|
||||
func (api *API) MWC() arinformers.MutatingWebhookConfigurationInformer {
|
||||
if api.mwc == nil {
|
||||
panic("MWC informer not configured")
|
||||
}
|
||||
return api.mwc
|
||||
}
|
||||
|
||||
// GetObjects returns a list of Kubernetes objects, given a namespace, type, and name.
|
||||
// If namespace is an empty string, match objects in all namespaces.
|
||||
// If name is an empty string, match all objects of the given type.
|
||||
|
|
|
|||
|
|
@ -33,5 +33,6 @@ func NewFakeAPI(configs ...string) (*API, error) {
|
|||
RC,
|
||||
RS,
|
||||
Svc,
|
||||
MWC,
|
||||
), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package fake
|
||||
|
||||
import (
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
// FakeClient is a fake clientset that implements the kubernetes.Interface.
|
||||
type Client struct {
|
||||
kubernetes.Interface
|
||||
}
|
||||
|
||||
// NewClient returns a fake Kubernetes clientset.
|
||||
func NewClient(kubeconfig string) (kubernetes.Interface, error) {
|
||||
client := fake.NewSimpleClientset()
|
||||
return &Client{client}, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
name: linkerd-secrets
|
||||
secret:
|
||||
secretName: ""
|
||||
optional: true
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
name: linkerd-trust-anchors
|
||||
configMap:
|
||||
name: linkerd-ca-bundle
|
||||
optional: true
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
args:
|
||||
- --incoming-proxy-port
|
||||
- 4143
|
||||
- --outgoing-proxy-port
|
||||
- 4140
|
||||
- --proxy-uid
|
||||
- 2102
|
||||
- --inbound-ports-to-ignore
|
||||
- 4190,4191
|
||||
image: gcr.io/linkerd-io/proxy-init:v18.8.4
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: linkerd-init
|
||||
resources: {}
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
privileged: false
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
env:
|
||||
- name: LINKERD2_PROXY_LOG
|
||||
value: warn,linkerd2_proxy=info
|
||||
- name: LINKERD2_PROXY_BIND_TIMEOUT
|
||||
value: 10s
|
||||
- name: LINKERD2_PROXY_CONTROL_URL
|
||||
value: tcp://proxy-api.linkerd.svc.cluster.local:8086
|
||||
- name: LINKERD2_PROXY_CONTROL_LISTENER
|
||||
value: tcp://0.0.0.0:4190
|
||||
- name: LINKERD2_PROXY_METRICS_LISTENER
|
||||
value: tcp://0.0.0.0:4191
|
||||
- name: LINKERD2_PROXY_OUTBOUND_LISTENER
|
||||
value: tcp://127.0.0.1:4140
|
||||
- name: LINKERD2_PROXY_INBOUND_LISTENER
|
||||
value: tcp://0.0.0.0:4143
|
||||
- name: LINKERD2_PROXY_POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: LINKERD2_PROXY_TLS_TRUST_ANCHORS
|
||||
value: /var/linkerd-io/trust-anchors/trust-anchors.pem
|
||||
- name: LINKERD2_PROXY_TLS_CERT
|
||||
value: /var/linkerd-io/identity/certificate.crt
|
||||
- name: LINKERD2_PROXY_TLS_PRIVATE_KEY
|
||||
value: /var/linkerd-io/identity/private-key.p8
|
||||
- name: LINKERD2_PROXY_TLS_POD_IDENTITY
|
||||
value: ""
|
||||
- name: LINKERD2_PROXY_CONTROLLER_NAMESPACE
|
||||
value: linkerd
|
||||
- name: LINKERD2_PROXY_TLS_CONTROLLER_IDENTITY
|
||||
value: ""
|
||||
image: gcr.io/linkerd-io/proxy:v18.8.4
|
||||
imagePullPolicy: IfNotPresent
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: 4191
|
||||
initialDelaySeconds: 10
|
||||
name: linkerd-proxy
|
||||
ports:
|
||||
- containerPort: 4143
|
||||
name: linkerd-proxy
|
||||
- containerPort: 4191
|
||||
name: linkerd-metrics
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: 4191
|
||||
initialDelaySeconds: 10
|
||||
resources: {}
|
||||
securityContext:
|
||||
runAsUser: 2102
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
volumeMounts:
|
||||
- mountPath: /var/linkerd-io/trust-anchors
|
||||
name: linkerd-trust-anchors
|
||||
readOnly: true
|
||||
- mountPath: /var/linkerd-io/identity
|
||||
name: linkerd-secrets
|
||||
readOnly: true
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: nginx
|
||||
namespace: kube-public
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
linkerd.io/auto-inject: completed
|
||||
annotations:
|
||||
created-by: isim
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: nginx
|
||||
namespace: kube-public
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
linkerd.io/auto-inject: disabled
|
||||
annotations:
|
||||
created-by: isim
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: nginx
|
||||
namespace: kube-public
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
annotations:
|
||||
created-by: isim
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: nginx
|
||||
namespace: kube-public
|
||||
labels:
|
||||
app: nginx
|
||||
linkerd.io/auto-inject: enabled
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
annotations:
|
||||
created-by: isim
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: nginx
|
||||
namespace: kube-public
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
annotations:
|
||||
created-by: isim
|
||||
spec:
|
||||
initContainers:
|
||||
- name: linkerd-init
|
||||
image: gcr.io/linkerd-io/proxy-init
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
- name: linkerd-proxy
|
||||
image: gc.io/linkerd-io/proxy
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
{
|
||||
"kind": "AdmissionReview",
|
||||
"apiVersion": "admission.k8s.io/v1beta1",
|
||||
"request": {
|
||||
"uid": "3c3c45ff-bee9-11e8-9c41-b4d755961931",
|
||||
"kind": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"kind": "Deployment"
|
||||
},
|
||||
"resource": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"resource": "deployments"
|
||||
},
|
||||
"namespace": "kube-public",
|
||||
"operation": "CREATE",
|
||||
"userInfo": {
|
||||
"username": "minikube-user",
|
||||
"groups": [
|
||||
"system:masters",
|
||||
"system:authenticated"
|
||||
]
|
||||
},
|
||||
"object": {
|
||||
"metadata": {
|
||||
"name": "nginx",
|
||||
"namespace": "kube-public",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "nginx"
|
||||
},
|
||||
"annotations": {
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"nginx\"},\"name\":\"nginx\",\"namespace\":\"kube-public\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"metadata\":{\"annotations\":{\"created-by\":\"isim\"},\"labels\":{\"app\":\"nginx\"}},\"spec\":{\"containers\":[{\"image\":\"nginx\",\"name\":\"nginx\",\"ports\":[{\"containerPort\":80,\"name\":\"http\"}]}]}}}}\n"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "nginx",
|
||||
"linkerd.io/auto-inject": "completed"
|
||||
},
|
||||
"annotations": {
|
||||
"created-by": "isim"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "nginx",
|
||||
"image": "nginx",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"containerPort": 80,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
],
|
||||
"resources": {},
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "Always"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"securityContext": {},
|
||||
"schedulerName": "default-scheduler"
|
||||
}
|
||||
},
|
||||
"strategy": {
|
||||
"type": "RollingUpdate",
|
||||
"rollingUpdate": {
|
||||
"maxUnavailable": "25%",
|
||||
"maxSurge": "25%"
|
||||
}
|
||||
},
|
||||
"revisionHistoryLimit": 10,
|
||||
"progressDeadlineSeconds": 600
|
||||
},
|
||||
"status": {}
|
||||
},
|
||||
"oldObject": null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
iapiVersion: admission.k8s.io/v1beta1
|
||||
kind: AdmissionReview
|
||||
request:
|
||||
kind:
|
||||
group: apps
|
||||
kind: Deployment
|
||||
version: v1
|
||||
namespace: kube-public
|
||||
object:
|
||||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: |
|
||||
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"nginx","namespace":"kube-public"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"nginx"}},"template":{"metadata":{"annotations":{"created-by":"isim"},"labels":{"app":"nginx"}},"spec":{"containers":[{"image":"nginx","name":"nginx","ports":[{"containerPort":80,"name":"http"}]}]}}}}
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: nginx
|
||||
name: nginx
|
||||
namespace: kube-public
|
||||
spec:
|
||||
progressDeadlineSeconds: 600
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 10
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
created-by: isim
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: nginx
|
||||
linkerd.io/auto-inject: completed
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
imagePullPolicy: Always
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
protocol: TCP
|
||||
resources: {}
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
dnsPolicy: ClusterFirst
|
||||
restartPolicy: Always
|
||||
schedulerName: default-scheduler
|
||||
securityContext: {}
|
||||
terminationGracePeriodSeconds: 30
|
||||
status: {}
|
||||
oldObject: null
|
||||
operation: CREATE
|
||||
resource:
|
||||
group: apps
|
||||
resource: deployments
|
||||
version: v1
|
||||
uid: 3c3c45ff-bee9-11e8-9c41-b4d755961931
|
||||
userInfo:
|
||||
groups:
|
||||
- system:masters
|
||||
- system:authenticated
|
||||
username: minikube-user
|
||||
response:
|
||||
allowed: true
|
||||
patch:
|
||||
patchType:
|
||||
uid: 3c3c45ff-bee9-11e8-9c41-b4d755961931
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
{
|
||||
"kind": "AdmissionReview",
|
||||
"apiVersion": "admission.k8s.io/v1beta1",
|
||||
"request": {
|
||||
"uid": "3c3c45ff-bee9-11e8-9c41-b4d755961931",
|
||||
"kind": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"kind": "Deployment"
|
||||
},
|
||||
"resource": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"resource": "deployments"
|
||||
},
|
||||
"namespace": "kube-public",
|
||||
"operation": "CREATE",
|
||||
"userInfo": {
|
||||
"username": "minikube-user",
|
||||
"groups": [
|
||||
"system:masters",
|
||||
"system:authenticated"
|
||||
]
|
||||
},
|
||||
"object": {
|
||||
"metadata": {
|
||||
"name": "nginx",
|
||||
"namespace": "kube-public",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "nginx"
|
||||
},
|
||||
"annotations": {
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"nginx\"},\"name\":\"nginx\",\"namespace\":\"kube-public\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"metadata\":{\"annotations\":{\"created-by\":\"isim\"},\"labels\":{\"app\":\"nginx\"}},\"spec\":{\"containers\":[{\"image\":\"nginx\",\"name\":\"nginx\",\"ports\":[{\"containerPort\":80,\"name\":\"http\"}]}]}}}}\n"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "nginx",
|
||||
"linkerd.io/auto-inject": "disabled"
|
||||
},
|
||||
"annotations": {
|
||||
"created-by": "isim"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "nginx",
|
||||
"image": "nginx",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"containerPort": 80,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
],
|
||||
"resources": {},
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "Always"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"securityContext": {},
|
||||
"schedulerName": "default-scheduler"
|
||||
}
|
||||
},
|
||||
"strategy": {
|
||||
"type": "RollingUpdate",
|
||||
"rollingUpdate": {
|
||||
"maxUnavailable": "25%",
|
||||
"maxSurge": "25%"
|
||||
}
|
||||
},
|
||||
"revisionHistoryLimit": 10,
|
||||
"progressDeadlineSeconds": 600
|
||||
},
|
||||
"status": {}
|
||||
},
|
||||
"oldObject": null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
iapiVersion: admission.k8s.io/v1beta1
|
||||
kind: AdmissionReview
|
||||
request:
|
||||
kind:
|
||||
group: apps
|
||||
kind: Deployment
|
||||
version: v1
|
||||
namespace: kube-public
|
||||
object:
|
||||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: |
|
||||
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"nginx","namespace":"kube-public"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"nginx"}},"template":{"metadata":{"annotations":{"created-by":"isim"},"labels":{"app":"nginx"}},"spec":{"containers":[{"image":"nginx","name":"nginx","ports":[{"containerPort":80,"name":"http"}]}]}}}}
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: nginx
|
||||
name: nginx
|
||||
namespace: kube-public
|
||||
spec:
|
||||
progressDeadlineSeconds: 600
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 10
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
created-by: isim
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: nginx
|
||||
linkerd.io/auto-inject: disabled
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
imagePullPolicy: Always
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
protocol: TCP
|
||||
resources: {}
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
dnsPolicy: ClusterFirst
|
||||
restartPolicy: Always
|
||||
schedulerName: default-scheduler
|
||||
securityContext: {}
|
||||
terminationGracePeriodSeconds: 30
|
||||
status: {}
|
||||
oldObject: null
|
||||
operation: CREATE
|
||||
resource:
|
||||
group: apps
|
||||
resource: deployments
|
||||
version: v1
|
||||
uid: 3c3c45ff-bee9-11e8-9c41-b4d755961931
|
||||
userInfo:
|
||||
groups:
|
||||
- system:masters
|
||||
- system:authenticated
|
||||
username: minikube-user
|
||||
response:
|
||||
allowed: true
|
||||
patch:
|
||||
patchType:
|
||||
uid: 3c3c45ff-bee9-11e8-9c41-b4d755961931
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
{
|
||||
"kind": "AdmissionReview",
|
||||
"apiVersion": "admission.k8s.io/v1beta1",
|
||||
"request": {
|
||||
"uid": "3c3c45ff-bee9-11e8-9c41-b4d755961931",
|
||||
"kind": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"kind": "Deployment"
|
||||
},
|
||||
"resource": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"resource": "deployments"
|
||||
},
|
||||
"namespace": "kube-public",
|
||||
"operation": "CREATE",
|
||||
"userInfo": {
|
||||
"username": "minikube-user",
|
||||
"groups": [
|
||||
"system:masters",
|
||||
"system:authenticated"
|
||||
]
|
||||
},
|
||||
"object": {
|
||||
"metadata": {
|
||||
"name": "nginx",
|
||||
"namespace": "kube-public",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "nginx"
|
||||
},
|
||||
"annotations": {
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"nginx\"},\"name\":\"nginx\",\"namespace\":\"kube-public\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"metadata\":{\"annotations\":{\"created-by\":\"isim\"},\"labels\":{\"app\":\"nginx\"}},\"spec\":{\"containers\":[{\"image\":\"nginx\",\"name\":\"nginx\",\"ports\":[{\"containerPort\":80,\"name\":\"http\"}]}]}}}}\n"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "nginx",
|
||||
"linkerd.io/auto-inject": "enabled"
|
||||
},
|
||||
"annotations": {
|
||||
"created-by": "isim"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "nginx",
|
||||
"image": "nginx",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"containerPort": 80,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
],
|
||||
"resources": {},
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "Always"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"securityContext": {},
|
||||
"schedulerName": "default-scheduler"
|
||||
}
|
||||
},
|
||||
"strategy": {
|
||||
"type": "RollingUpdate",
|
||||
"rollingUpdate": {
|
||||
"maxUnavailable": "25%",
|
||||
"maxSurge": "25%"
|
||||
}
|
||||
},
|
||||
"revisionHistoryLimit": 10,
|
||||
"progressDeadlineSeconds": 600
|
||||
},
|
||||
"status": {}
|
||||
},
|
||||
"oldObject": null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
iapiVersion: admission.k8s.io/v1beta1
|
||||
kind: AdmissionReview
|
||||
request:
|
||||
kind:
|
||||
group: apps
|
||||
kind: Deployment
|
||||
version: v1
|
||||
namespace: kube-public
|
||||
object:
|
||||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: |
|
||||
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"nginx","namespace":"kube-public"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"nginx"}},"template":{"metadata":{"annotations":{"created-by":"isim"},"labels":{"app":"nginx"}},"spec":{"containers":[{"image":"nginx","name":"nginx","ports":[{"containerPort":80,"name":"http"}]}]}}}}
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: nginx
|
||||
name: nginx
|
||||
namespace: kube-public
|
||||
spec:
|
||||
progressDeadlineSeconds: 600
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 10
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
created-by: isim
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: nginx
|
||||
linkerd.io/auto-inject: enabled
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
imagePullPolicy: Always
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
protocol: TCP
|
||||
resources: {}
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
dnsPolicy: ClusterFirst
|
||||
restartPolicy: Always
|
||||
schedulerName: default-scheduler
|
||||
securityContext: {}
|
||||
terminationGracePeriodSeconds: 30
|
||||
status: {}
|
||||
oldObject: null
|
||||
operation: CREATE
|
||||
resource:
|
||||
group: apps
|
||||
resource: deployments
|
||||
version: v1
|
||||
uid: 3c3c45ff-bee9-11e8-9c41-b4d755961931
|
||||
userInfo:
|
||||
groups:
|
||||
- system:masters
|
||||
- system:authenticated
|
||||
username: minikube-user
|
||||
response:
|
||||
allowed: true
|
||||
patch: W3sib3AiOiJhZGQiLCJwYXRoIjoiL3NwZWMvdGVtcGxhdGUvc3BlYy9jb250YWluZXJzLzAiLCJ2YWx1ZSI6eyJuYW1lIjoibGlua2VyZC1wcm94eSIsImltYWdlIjoiZ2NyLmlvL2xpbmtlcmQtaW8vcHJveHk6djE4LjguNCIsInBvcnRzIjpbeyJuYW1lIjoibGlua2VyZC1wcm94eSIsImNvbnRhaW5lclBvcnQiOjQxNDN9LHsibmFtZSI6ImxpbmtlcmQtbWV0cmljcyIsImNvbnRhaW5lclBvcnQiOjQxOTF9XSwiZW52IjpbeyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfTE9HIiwidmFsdWUiOiJ3YXJuLGxpbmtlcmQyX3Byb3h5PWluZm8ifSx7Im5hbWUiOiJMSU5LRVJEMl9QUk9YWV9CSU5EX1RJTUVPVVQiLCJ2YWx1ZSI6IjEwcyJ9LHsibmFtZSI6IkxJTktFUkQyX1BST1hZX0NPTlRST0xfVVJMIiwidmFsdWUiOiJ0Y3A6Ly9wcm94eS1hcGkubGlua2VyZC5zdmMuY2x1c3Rlci5sb2NhbDo4MDg2In0seyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfQ09OVFJPTF9MSVNURU5FUiIsInZhbHVlIjoidGNwOi8vMC4wLjAuMDo0MTkwIn0seyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfTUVUUklDU19MSVNURU5FUiIsInZhbHVlIjoidGNwOi8vMC4wLjAuMDo0MTkxIn0seyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfT1VUQk9VTkRfTElTVEVORVIiLCJ2YWx1ZSI6InRjcDovLzEyNy4wLjAuMTo0MTQwIn0seyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfSU5CT1VORF9MSVNURU5FUiIsInZhbHVlIjoidGNwOi8vMC4wLjAuMDo0MTQzIn0seyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfUE9EX05BTUVTUEFDRSIsInZhbHVlRnJvbSI6eyJmaWVsZFJlZiI6eyJmaWVsZFBhdGgiOiJtZXRhZGF0YS5uYW1lc3BhY2UifX19LHsibmFtZSI6IkxJTktFUkQyX1BST1hZX1RMU19UUlVTVF9BTkNIT1JTIiwidmFsdWUiOiIvdmFyL2xpbmtlcmQtaW8vdHJ1c3QtYW5jaG9ycy90cnVzdC1hbmNob3JzLnBlbSJ9LHsibmFtZSI6IkxJTktFUkQyX1BST1hZX1RMU19DRVJUIiwidmFsdWUiOiIvdmFyL2xpbmtlcmQtaW8vaWRlbnRpdHkvY2VydGlmaWNhdGUuY3J0In0seyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfVExTX1BSSVZBVEVfS0VZIiwidmFsdWUiOiIvdmFyL2xpbmtlcmQtaW8vaWRlbnRpdHkvcHJpdmF0ZS1rZXkucDgifSx7Im5hbWUiOiJMSU5LRVJEMl9QUk9YWV9UTFNfUE9EX0lERU5USVRZIiwidmFsdWUiOiJuZ2lueC5kZXBsb3ltZW50Lmt1YmUtcHVibGljLmxpbmtlcmQtbWFuYWdlZC5saW5rZXJkLnN2Yy5jbHVzdGVyLmxvY2FsIn0seyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfQ09OVFJPTExFUl9OQU1FU1BBQ0UiLCJ2YWx1ZSI6ImxpbmtlcmQifSx7Im5hbWUiOiJMSU5LRVJEMl9QUk9YWV9UTFNfQ09OVFJPTExFUl9JREVOVElUWSIsInZhbHVlIjoiY29udHJvbGxlci5kZXBsb3ltZW50LmxpbmtlcmQubGlua2VyZC1tYW5hZ2VkLmxpbmtlcmQuc3ZjLmNsdXN0ZXIubG9jYWwifV0sInJlc291cmNlcyI6e30sInZvbHVtZU1vdW50cyI6W3sibmFtZSI6ImxpbmtlcmQtdHJ1c3QtYW5jaG9ycyIsInJlYWRPbmx5Ijp0cnVlLCJtb3VudFBhdGgiOiIvdmFyL2xpbmtlcmQtaW8vdHJ1c3QtYW5jaG9ycyJ9LHsibmFtZSI6ImxpbmtlcmQtc2VjcmV0cyIsInJlYWRPbmx5Ijp0cnVlLCJtb3VudFBhdGgiOiIvdmFyL2xpbmtlcmQtaW8vaWRlbnRpdHkifV0sImxpdmVuZXNzUHJvYmUiOnsiaHR0cEdldCI6eyJwYXRoIjoiL21ldHJpY3MiLCJwb3J0Ijo0MTkxfSwiaW5pdGlhbERlbGF5U2Vjb25kcyI6MTB9LCJyZWFkaW5lc3NQcm9iZSI6eyJodHRwR2V0Ijp7InBhdGgiOiIvbWV0cmljcyIsInBvcnQiOjQxOTF9LCJpbml0aWFsRGVsYXlTZWNvbmRzIjoxMH0sInRlcm1pbmF0aW9uTWVzc2FnZVBvbGljeSI6IkZhbGxiYWNrVG9Mb2dzT25FcnJvciIsImltYWdlUHVsbFBvbGljeSI6IklmTm90UHJlc2VudCIsInNlY3VyaXR5Q29udGV4dCI6eyJydW5Bc1VzZXIiOjIxMDJ9fX0seyJvcCI6ImFkZCIsInBhdGgiOiIvc3BlYy90ZW1wbGF0ZS9zcGVjL2luaXRDb250YWluZXJzIiwidmFsdWUiOltdfSx7Im9wIjoiYWRkIiwicGF0aCI6Ii9zcGVjL3RlbXBsYXRlL3NwZWMvaW5pdENvbnRhaW5lcnMvMCIsInZhbHVlIjp7Im5hbWUiOiJsaW5rZXJkLWluaXQiLCJpbWFnZSI6Imdjci5pby9saW5rZXJkLWlvL3Byb3h5LWluaXQ6djE4LjguNCIsImFyZ3MiOlsiLS1pbmNvbWluZy1wcm94eS1wb3J0IiwiNDE0MyIsIi0tb3V0Z29pbmctcHJveHktcG9ydCIsIjQxNDAiLCItLXByb3h5LXVpZCIsIjIxMDIiLCItLWluYm91bmQtcG9ydHMtdG8taWdub3JlIiwiNDE5MCw0MTkxIl0sInJlc291cmNlcyI6e30sInRlcm1pbmF0aW9uTWVzc2FnZVBvbGljeSI6IkZhbGxiYWNrVG9Mb2dzT25FcnJvciIsImltYWdlUHVsbFBvbGljeSI6IklmTm90UHJlc2VudCIsInNlY3VyaXR5Q29udGV4dCI6eyJjYXBhYmlsaXRpZXMiOnsiYWRkIjpbIk5FVF9BRE1JTiJdfSwicHJpdmlsZWdlZCI6ZmFsc2V9fX0seyJvcCI6ImFkZCIsInBhdGgiOiIvc3BlYy90ZW1wbGF0ZS9zcGVjL3ZvbHVtZXMiLCJ2YWx1ZSI6W119LHsib3AiOiJhZGQiLCJwYXRoIjoiL3NwZWMvdGVtcGxhdGUvc3BlYy92b2x1bWVzLzAiLCJ2YWx1ZSI6eyJuYW1lIjoibGlua2VyZC10cnVzdC1hbmNob3JzIiwiY29uZmlnTWFwIjp7Im5hbWUiOiJsaW5rZXJkLWNhLWJ1bmRsZSIsIm9wdGlvbmFsIjp0cnVlfX19LHsib3AiOiJhZGQiLCJwYXRoIjoiL3NwZWMvdGVtcGxhdGUvc3BlYy92b2x1bWVzLzAiLCJ2YWx1ZSI6eyJuYW1lIjoibGlua2VyZC1zZWNyZXRzIiwic2VjcmV0Ijp7InNlY3JldE5hbWUiOiJuZ2lueC1kZXBsb3ltZW50LXRscy1saW5rZXJkLWlvIiwib3B0aW9uYWwiOnRydWV9fX0seyJvcCI6ImFkZCIsInBhdGgiOiIvc3BlYy90ZW1wbGF0ZS9tZXRhZGF0YS9sYWJlbHMiLCJ2YWx1ZSI6eyJsaW5rZXJkLmlvL2NvbnRyb2wtcGxhbmUtbnMiOiJsaW5rZXJkIiwibGlua2VyZC5pby9wcm94eS1kZXBsb3ltZW50IjoibmdpbngifX0seyJvcCI6ImFkZCIsInBhdGgiOiIvc3BlYy90ZW1wbGF0ZS9tZXRhZGF0YS9hbm5vdGF0aW9ucyIsInZhbHVlIjp7ImxpbmtlcmQuaW8vY3JlYXRlZC1ieSI6ImxpbmtlcmQvcHJveHktaW5qZWN0b3IgdjE4LjguNCIsImxpbmtlcmQuaW8vcHJveHktdmVyc2lvbiI6InYxOC44LjQifX1d
|
||||
patchType: JSONPatch
|
||||
uid: 3c3c45ff-bee9-11e8-9c41-b4d755961931
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
args:
|
||||
- --incoming-proxy-port
|
||||
- 4143
|
||||
- --outgoing-proxy-port
|
||||
- 4140
|
||||
- --proxy-uid
|
||||
- 2102
|
||||
- --inbound-ports-to-ignore
|
||||
- 4190,4191
|
||||
image: gcr.io/linkerd-io/proxy-init:v18.8.4
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: linkerd-init
|
||||
resources: {}
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
privileged: false
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
name: linkerd-secrets
|
||||
secret:
|
||||
secretName: nginx-deployment-tls-linkerd-io
|
||||
optional: true
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
{
|
||||
"kind": "AdmissionReview",
|
||||
"apiVersion": "admission.k8s.io/v1beta1",
|
||||
"request": {
|
||||
"uid": "3c3c45ff-bee9-11e8-9c41-b4d755961931",
|
||||
"kind": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"kind": "Deployment"
|
||||
},
|
||||
"resource": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"resource": "deployments"
|
||||
},
|
||||
"namespace": "kube-public",
|
||||
"operation": "CREATE",
|
||||
"userInfo": {
|
||||
"username": "minikube-user",
|
||||
"groups": [
|
||||
"system:masters",
|
||||
"system:authenticated"
|
||||
]
|
||||
},
|
||||
"object": {
|
||||
"metadata": {
|
||||
"name": "nginx",
|
||||
"namespace": "kube-public",
|
||||
"creationTimestamp": null,
|
||||
"annotations": {
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"nginx\"},\"name\":\"nginx\",\"namespace\":\"kube-public\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"metadata\":{\"annotations\":{\"created-by\":\"isim\"},\"labels\":{\"app\":\"nginx\"}},\"spec\":{\"containers\":[{\"image\":\"nginx\",\"name\":\"nginx\",\"ports\":[{\"containerPort\":80,\"name\":\"http\"}]}]}}}}\n"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "nginx"
|
||||
},
|
||||
"annotations": {
|
||||
"created-by": "isim"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "nginx",
|
||||
"image": "nginx",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"containerPort": 80,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
],
|
||||
"resources": {},
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "Always"
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Always",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"securityContext": {},
|
||||
"schedulerName": "default-scheduler"
|
||||
}
|
||||
},
|
||||
"strategy": {
|
||||
"type": "RollingUpdate",
|
||||
"rollingUpdate": {
|
||||
"maxUnavailable": "25%",
|
||||
"maxSurge": "25%"
|
||||
}
|
||||
},
|
||||
"revisionHistoryLimit": 10,
|
||||
"progressDeadlineSeconds": 600
|
||||
},
|
||||
"status": {}
|
||||
},
|
||||
"oldObject": null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
iapiVersion: admission.k8s.io/v1beta1
|
||||
kind: AdmissionReview
|
||||
request:
|
||||
kind:
|
||||
group: apps
|
||||
kind: Deployment
|
||||
version: v1
|
||||
namespace: kube-public
|
||||
object:
|
||||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: |
|
||||
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"nginx","namespace":"kube-public"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"nginx"}},"template":{"metadata":{"annotations":{"created-by":"isim"},"labels":{"app":"nginx"}},"spec":{"containers":[{"image":"nginx","name":"nginx","ports":[{"containerPort":80,"name":"http"}]}]}}}}
|
||||
creationTimestamp: null
|
||||
name: nginx
|
||||
namespace: kube-public
|
||||
spec:
|
||||
progressDeadlineSeconds: 600
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 10
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
created-by: isim
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
imagePullPolicy: Always
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
protocol: TCP
|
||||
resources: {}
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
dnsPolicy: ClusterFirst
|
||||
restartPolicy: Always
|
||||
schedulerName: default-scheduler
|
||||
securityContext: {}
|
||||
terminationGracePeriodSeconds: 30
|
||||
status: {}
|
||||
oldObject: null
|
||||
operation: CREATE
|
||||
resource:
|
||||
group: apps
|
||||
resource: deployments
|
||||
version: v1
|
||||
uid: 3c3c45ff-bee9-11e8-9c41-b4d755961931
|
||||
userInfo:
|
||||
groups:
|
||||
- system:masters
|
||||
- system:authenticated
|
||||
username: minikube-user
|
||||
response:
|
||||
allowed: true
|
||||
patch: W3sib3AiOiJhZGQiLCJwYXRoIjoiL3NwZWMvdGVtcGxhdGUvc3BlYy9jb250YWluZXJzLzAiLCJ2YWx1ZSI6eyJuYW1lIjoibGlua2VyZC1wcm94eSIsImltYWdlIjoiZ2NyLmlvL2xpbmtlcmQtaW8vcHJveHk6djE4LjguNCIsInBvcnRzIjpbeyJuYW1lIjoibGlua2VyZC1wcm94eSIsImNvbnRhaW5lclBvcnQiOjQxNDN9LHsibmFtZSI6ImxpbmtlcmQtbWV0cmljcyIsImNvbnRhaW5lclBvcnQiOjQxOTF9XSwiZW52IjpbeyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfTE9HIiwidmFsdWUiOiJ3YXJuLGxpbmtlcmQyX3Byb3h5PWluZm8ifSx7Im5hbWUiOiJMSU5LRVJEMl9QUk9YWV9CSU5EX1RJTUVPVVQiLCJ2YWx1ZSI6IjEwcyJ9LHsibmFtZSI6IkxJTktFUkQyX1BST1hZX0NPTlRST0xfVVJMIiwidmFsdWUiOiJ0Y3A6Ly9wcm94eS1hcGkubGlua2VyZC5zdmMuY2x1c3Rlci5sb2NhbDo4MDg2In0seyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfQ09OVFJPTF9MSVNURU5FUiIsInZhbHVlIjoidGNwOi8vMC4wLjAuMDo0MTkwIn0seyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfTUVUUklDU19MSVNURU5FUiIsInZhbHVlIjoidGNwOi8vMC4wLjAuMDo0MTkxIn0seyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfT1VUQk9VTkRfTElTVEVORVIiLCJ2YWx1ZSI6InRjcDovLzEyNy4wLjAuMTo0MTQwIn0seyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfSU5CT1VORF9MSVNURU5FUiIsInZhbHVlIjoidGNwOi8vMC4wLjAuMDo0MTQzIn0seyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfUE9EX05BTUVTUEFDRSIsInZhbHVlRnJvbSI6eyJmaWVsZFJlZiI6eyJmaWVsZFBhdGgiOiJtZXRhZGF0YS5uYW1lc3BhY2UifX19LHsibmFtZSI6IkxJTktFUkQyX1BST1hZX1RMU19UUlVTVF9BTkNIT1JTIiwidmFsdWUiOiIvdmFyL2xpbmtlcmQtaW8vdHJ1c3QtYW5jaG9ycy90cnVzdC1hbmNob3JzLnBlbSJ9LHsibmFtZSI6IkxJTktFUkQyX1BST1hZX1RMU19DRVJUIiwidmFsdWUiOiIvdmFyL2xpbmtlcmQtaW8vaWRlbnRpdHkvY2VydGlmaWNhdGUuY3J0In0seyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfVExTX1BSSVZBVEVfS0VZIiwidmFsdWUiOiIvdmFyL2xpbmtlcmQtaW8vaWRlbnRpdHkvcHJpdmF0ZS1rZXkucDgifSx7Im5hbWUiOiJMSU5LRVJEMl9QUk9YWV9UTFNfUE9EX0lERU5USVRZIiwidmFsdWUiOiJuZ2lueC5kZXBsb3ltZW50Lmt1YmUtcHVibGljLmxpbmtlcmQtbWFuYWdlZC5saW5rZXJkLnN2Yy5jbHVzdGVyLmxvY2FsIn0seyJuYW1lIjoiTElOS0VSRDJfUFJPWFlfQ09OVFJPTExFUl9OQU1FU1BBQ0UiLCJ2YWx1ZSI6ImxpbmtlcmQifSx7Im5hbWUiOiJMSU5LRVJEMl9QUk9YWV9UTFNfQ09OVFJPTExFUl9JREVOVElUWSIsInZhbHVlIjoiY29udHJvbGxlci5kZXBsb3ltZW50LmxpbmtlcmQubGlua2VyZC1tYW5hZ2VkLmxpbmtlcmQuc3ZjLmNsdXN0ZXIubG9jYWwifV0sInJlc291cmNlcyI6e30sInZvbHVtZU1vdW50cyI6W3sibmFtZSI6ImxpbmtlcmQtdHJ1c3QtYW5jaG9ycyIsInJlYWRPbmx5Ijp0cnVlLCJtb3VudFBhdGgiOiIvdmFyL2xpbmtlcmQtaW8vdHJ1c3QtYW5jaG9ycyJ9LHsibmFtZSI6ImxpbmtlcmQtc2VjcmV0cyIsInJlYWRPbmx5Ijp0cnVlLCJtb3VudFBhdGgiOiIvdmFyL2xpbmtlcmQtaW8vaWRlbnRpdHkifV0sImxpdmVuZXNzUHJvYmUiOnsiaHR0cEdldCI6eyJwYXRoIjoiL21ldHJpY3MiLCJwb3J0Ijo0MTkxfSwiaW5pdGlhbERlbGF5U2Vjb25kcyI6MTB9LCJyZWFkaW5lc3NQcm9iZSI6eyJodHRwR2V0Ijp7InBhdGgiOiIvbWV0cmljcyIsInBvcnQiOjQxOTF9LCJpbml0aWFsRGVsYXlTZWNvbmRzIjoxMH0sInRlcm1pbmF0aW9uTWVzc2FnZVBvbGljeSI6IkZhbGxiYWNrVG9Mb2dzT25FcnJvciIsImltYWdlUHVsbFBvbGljeSI6IklmTm90UHJlc2VudCIsInNlY3VyaXR5Q29udGV4dCI6eyJydW5Bc1VzZXIiOjIxMDJ9fX0seyJvcCI6ImFkZCIsInBhdGgiOiIvc3BlYy90ZW1wbGF0ZS9zcGVjL2luaXRDb250YWluZXJzIiwidmFsdWUiOltdfSx7Im9wIjoiYWRkIiwicGF0aCI6Ii9zcGVjL3RlbXBsYXRlL3NwZWMvaW5pdENvbnRhaW5lcnMvMCIsInZhbHVlIjp7Im5hbWUiOiJsaW5rZXJkLWluaXQiLCJpbWFnZSI6Imdjci5pby9saW5rZXJkLWlvL3Byb3h5LWluaXQ6djE4LjguNCIsImFyZ3MiOlsiLS1pbmNvbWluZy1wcm94eS1wb3J0IiwiNDE0MyIsIi0tb3V0Z29pbmctcHJveHktcG9ydCIsIjQxNDAiLCItLXByb3h5LXVpZCIsIjIxMDIiLCItLWluYm91bmQtcG9ydHMtdG8taWdub3JlIiwiNDE5MCw0MTkxIl0sInJlc291cmNlcyI6e30sInRlcm1pbmF0aW9uTWVzc2FnZVBvbGljeSI6IkZhbGxiYWNrVG9Mb2dzT25FcnJvciIsImltYWdlUHVsbFBvbGljeSI6IklmTm90UHJlc2VudCIsInNlY3VyaXR5Q29udGV4dCI6eyJjYXBhYmlsaXRpZXMiOnsiYWRkIjpbIk5FVF9BRE1JTiJdfSwicHJpdmlsZWdlZCI6ZmFsc2V9fX0seyJvcCI6ImFkZCIsInBhdGgiOiIvc3BlYy90ZW1wbGF0ZS9zcGVjL3ZvbHVtZXMiLCJ2YWx1ZSI6W119LHsib3AiOiJhZGQiLCJwYXRoIjoiL3NwZWMvdGVtcGxhdGUvc3BlYy92b2x1bWVzLzAiLCJ2YWx1ZSI6eyJuYW1lIjoibGlua2VyZC10cnVzdC1hbmNob3JzIiwiY29uZmlnTWFwIjp7Im5hbWUiOiJsaW5rZXJkLWNhLWJ1bmRsZSIsIm9wdGlvbmFsIjp0cnVlfX19LHsib3AiOiJhZGQiLCJwYXRoIjoiL3NwZWMvdGVtcGxhdGUvc3BlYy92b2x1bWVzLzAiLCJ2YWx1ZSI6eyJuYW1lIjoibGlua2VyZC1zZWNyZXRzIiwic2VjcmV0Ijp7InNlY3JldE5hbWUiOiJuZ2lueC1kZXBsb3ltZW50LXRscy1saW5rZXJkLWlvIiwib3B0aW9uYWwiOnRydWV9fX0seyJvcCI6ImFkZCIsInBhdGgiOiIvc3BlYy90ZW1wbGF0ZS9tZXRhZGF0YS9sYWJlbHMiLCJ2YWx1ZSI6eyJsaW5rZXJkLmlvL2NvbnRyb2wtcGxhbmUtbnMiOiJsaW5rZXJkIiwibGlua2VyZC5pby9wcm94eS1kZXBsb3ltZW50IjoibmdpbngifX0seyJvcCI6ImFkZCIsInBhdGgiOiIvc3BlYy90ZW1wbGF0ZS9tZXRhZGF0YS9hbm5vdGF0aW9ucyIsInZhbHVlIjp7ImxpbmtlcmQuaW8vY3JlYXRlZC1ieSI6ImxpbmtlcmQvcHJveHktaW5qZWN0b3IgdjE4LjguNCIsImxpbmtlcmQuaW8vcHJveHktdmVyc2lvbiI6InYxOC44LjQifX1d
|
||||
patchType: JSONPatch
|
||||
uid: 3c3c45ff-bee9-11e8-9c41-b4d755961931
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
env:
|
||||
- name: LINKERD2_PROXY_LOG
|
||||
value: warn,linkerd2_proxy=info
|
||||
- name: LINKERD2_PROXY_BIND_TIMEOUT
|
||||
value: 10s
|
||||
- name: LINKERD2_PROXY_CONTROL_URL
|
||||
value: tcp://proxy-api.linkerd.svc.cluster.local:8086
|
||||
- name: LINKERD2_PROXY_CONTROL_LISTENER
|
||||
value: tcp://0.0.0.0:4190
|
||||
- name: LINKERD2_PROXY_METRICS_LISTENER
|
||||
value: tcp://0.0.0.0:4191
|
||||
- name: LINKERD2_PROXY_OUTBOUND_LISTENER
|
||||
value: tcp://127.0.0.1:4140
|
||||
- name: LINKERD2_PROXY_INBOUND_LISTENER
|
||||
value: tcp://0.0.0.0:4143
|
||||
- name: LINKERD2_PROXY_POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: LINKERD2_PROXY_TLS_TRUST_ANCHORS
|
||||
value: /var/linkerd-io/trust-anchors/trust-anchors.pem
|
||||
- name: LINKERD2_PROXY_TLS_CERT
|
||||
value: /var/linkerd-io/identity/certificate.crt
|
||||
- name: LINKERD2_PROXY_TLS_PRIVATE_KEY
|
||||
value: /var/linkerd-io/identity/private-key.p8
|
||||
- name: LINKERD2_PROXY_TLS_POD_IDENTITY
|
||||
value: nginx.deployment.default.linkerd-managed.linkerd.svc.cluster.local
|
||||
- name: LINKERD2_PROXY_CONTROLLER_NAMESPACE
|
||||
value: linkerd
|
||||
- name: LINKERD2_PROXY_TLS_CONTROLLER_IDENTITY
|
||||
value: controller.deployment.linkerd.linkerd-managed.linkerd.svc.cluster.local
|
||||
image: gcr.io/linkerd-io/proxy:v18.8.4
|
||||
imagePullPolicy: IfNotPresent
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: 4191
|
||||
initialDelaySeconds: 10
|
||||
name: linkerd-proxy
|
||||
ports:
|
||||
- containerPort: 4143
|
||||
name: linkerd-proxy
|
||||
- containerPort: 4191
|
||||
name: linkerd-metrics
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: 4191
|
||||
initialDelaySeconds: 10
|
||||
resources: {}
|
||||
securityContext:
|
||||
runAsUser: 2102
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
volumeMounts:
|
||||
- mountPath: /var/linkerd-io/trust-anchors
|
||||
name: linkerd-trust-anchors
|
||||
readOnly: true
|
||||
- mountPath: /var/linkerd-io/identity
|
||||
name: linkerd-secrets
|
||||
readOnly: true
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
name: linkerd-trust-anchors
|
||||
configMap:
|
||||
name: linkerd-ca-bundle
|
||||
optional: true
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: kube-public
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: linkerd
|
||||
labels:
|
||||
linkerd.io/auto-inject: enabled
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
package fake
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
yaml "github.com/ghodss/yaml"
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultControllerNamespace = "linkerd"
|
||||
DefaultNamespace = "default"
|
||||
FileProxySpec = "fake/data/config-proxy.yaml"
|
||||
FileProxyInitSpec = "fake/data/config-proxy-init.yaml"
|
||||
FileTLSTrustAnchorVolumeSpec = "fake/data/config-linkerd-trust-anchors.yaml"
|
||||
FileTLSIdentityVolumeSpec = "fake/data/config-linkerd-secrets.yaml"
|
||||
)
|
||||
|
||||
// Factory is a factory that can convert in-file YAML content into Kubernetes
|
||||
// API objects.
|
||||
type Factory struct {
|
||||
rootDir string
|
||||
}
|
||||
|
||||
// NewFactory returns a new instance of Fixture.
|
||||
func NewFactory() *Factory {
|
||||
return &Factory{rootDir: filepath.Join("fake", "data")}
|
||||
}
|
||||
|
||||
// HTTPRequestBody returns the content of the specified file as a slice of
|
||||
// bytes. If the file doesn't exist in the 'fake/data' folder, an error will be
|
||||
// returned.
|
||||
func (f *Factory) HTTPRequestBody(filename string) ([]byte, error) {
|
||||
return ioutil.ReadFile(filepath.Join(f.rootDir, filename))
|
||||
}
|
||||
|
||||
// AdmissionReview returns the content of the specified file as an
|
||||
// AdmissionReview type. An error will be returned if:
|
||||
// i. the file doesn't exist in the 'fake/data' folder or,
|
||||
// ii. the file content isn't a valid YAML structure that can be unmarshalled
|
||||
// into AdmissionReview type
|
||||
func (f *Factory) AdmissionReview(filename string) (*admissionv1beta1.AdmissionReview, error) {
|
||||
b, err := ioutil.ReadFile(filepath.Join(f.rootDir, filename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var admissionReview admissionv1beta1.AdmissionReview
|
||||
if err := yaml.Unmarshal(b, &admissionReview); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &admissionReview, nil
|
||||
}
|
||||
|
||||
// Deployment returns the content of the specified file as a Deployment type. An
|
||||
// error will be returned if:
|
||||
// i. the file doesn't exist in the 'fake/data' folder or
|
||||
// ii. the file content isn't a valid YAML structure that can be unmarshalled
|
||||
// into Deployment type
|
||||
func (f *Factory) Deployment(filename string) (*appsv1.Deployment, error) {
|
||||
b, err := ioutil.ReadFile(filepath.Join(f.rootDir, filename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var deployment appsv1.Deployment
|
||||
if err := yaml.Unmarshal(b, &deployment); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &deployment, nil
|
||||
}
|
||||
|
||||
// Container returns the content of the specified file as a Container type. An
|
||||
// error will be returned if:
|
||||
// i. the file doesn't exist in the 'fake/data' folder or
|
||||
// ii. the file content isn't a valid YAML structure that can be unmarshalled
|
||||
// into Container type
|
||||
func (f *Factory) Container(filename string) (*corev1.Container, error) {
|
||||
b, err := ioutil.ReadFile(filepath.Join(f.rootDir, filename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var container corev1.Container
|
||||
if err := yaml.Unmarshal(b, &container); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &container, nil
|
||||
}
|
||||
|
||||
// ConfigMap returns the content of the specified file as a ConfigMap type. An
|
||||
// error will be returned if:
|
||||
// i. the file doesn't exist in the 'fake/data' folder or
|
||||
// ii. the file content isn't a valid YAML structure that can be unmarshalled
|
||||
// into ConfigMap type
|
||||
func (f *Factory) ConfigMap(filename string) (*corev1.ConfigMap, error) {
|
||||
b, err := ioutil.ReadFile(filepath.Join(f.rootDir, filename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var configMap corev1.ConfigMap
|
||||
if err := yaml.Unmarshal(b, &configMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &configMap, nil
|
||||
}
|
||||
|
||||
// Namespace returns the content of the specified file as a Namespace type. An
|
||||
// error will be returned if:
|
||||
// i. the file doesn't exist in the 'fake/data' folder or
|
||||
// ii. the file content isn't a valid YAML structure that can be unmarshalled
|
||||
// into Namespace type
|
||||
func (f *Factory) Namespace(filename string) (*corev1.Namespace, error) {
|
||||
b, err := ioutil.ReadFile(filepath.Join(f.rootDir, filename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var namespace corev1.Namespace
|
||||
if err := yaml.Unmarshal(b, &namespace); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &namespace, nil
|
||||
}
|
||||
|
||||
// Volume returns the content of the specified file as a Volume type. An error
|
||||
// will be returned if:
|
||||
// i. the file doesn't exist in the 'fake/data' folder or
|
||||
// ii. the file content isn't a valid YAML structure that can be unmarshalled
|
||||
// into Volume type
|
||||
func (f *Factory) Volume(filename string) (*corev1.Volume, error) {
|
||||
b, err := ioutil.ReadFile(filepath.Join(f.rootDir, filename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var volume corev1.Volume
|
||||
if err := yaml.Unmarshal(b, &volume); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &volume, nil
|
||||
}
|
||||
|
||||
// CATrustAnchors creates a fake CA trust anchors and returns the name of the
|
||||
// temporary file. Caller is responsible for deleting the file once it's done.
|
||||
func (f *Factory) CATrustAnchors() (string, error) {
|
||||
file, err := ioutil.TempFile("", "linkerd-fake-trust-anchors.pem")
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
trustAnchorsPEM := []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIBTzCB9qADAgECAgEBMAoGCCqGSM49BAMCMCcxJTAjBgNVBAMTHENsdXN0ZXIt
|
||||
bG9jYWwgTWFuYWdlZCBQb2QgQ0EwHhcNMTgwOTA0MTQyMjM3WhcNMTkwOTA1MTQy
|
||||
MjM3WjAnMSUwIwYDVQQDExxDbHVzdGVyLWxvY2FsIE1hbmFnZWQgUG9kIENBMFkw
|
||||
EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEj0LP/rUU/htkvPasq/+OIytK8WPI2zWt
|
||||
4XkFH6eIap/wgOWJ+UMsSWz15Sj0QgnVzazFQ0BjXSlFGJVTkIMoEaMTMBEwDwYD
|
||||
VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNIADBFAiAWLxJI8P/Pn/fTU9wMEY6D
|
||||
qztZiU7GJkLZDF/Xr6Su6wIhAPVznxMv1uA4P8hFRDdb4TyZ+3xI64a5UwoBnk99
|
||||
gvKX
|
||||
-----END CERTIFICATE-----`)
|
||||
if err := ioutil.WriteFile(file.Name(), trustAnchorsPEM, 0400); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return file.Name(), nil
|
||||
}
|
||||
|
||||
// CertFile returns a dummy base64-encoded PEM certificate file path. Caller is
|
||||
// responsible for deleting the certificate after use by calling os.Remove(cert).
|
||||
// This certificate matches the key generated by the Key() method.
|
||||
func (f *Factory) CertFile() (string, error) {
|
||||
cert := "MIIBcDCCARWgAwIBAgIBHDAKBggqhkjOPQQDAjAnMSUwIwYDVQQDExxDbHVzdGVyLWxvY2FsIE1hbmFnZWQgUG9kIENBMB4XDTE4MDkxNDE2NDg1NFoXDTE5MDkxNTE2NDg1NFowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDTSUF/5+Co7z4NbV5Ui7XbhiFixQccyTKOYHbk4sKyMqwE9UNRBB5ILh3nEQxhaSswd+Yxxs1M393nHb4xkZW+jWTBXMFUGA1UdEQEB/wRLMEmCR2NvbnRyb2xsZXIuZGVwbG95bWVudC5saW5rZXJkLmxpbmtlcmQtbWFuYWdlZC5saW5rZXJkLnN2Yy5jbHVzdGVyLmxvY2FsMAoGCCqGSM49BAMCA0kAMEYCIQDU6UtUxLQJ/TmWqzVFspXvD0e78xe80koj0ib9wARxIQIhAPVyv+1GaT472qgDXb+HglDK7ZeacEjCh9rEenefJd2w"
|
||||
decodedCert, err := base64.StdEncoding.DecodeString(cert)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
certFile, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(certFile.Name(), decodedCert, 0); err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return certFile.Name(), nil
|
||||
}
|
||||
|
||||
// KeyFile returns a dummy base64-encoded private key file path. Caller is
|
||||
// responsible for deleting the certificate after use by calling os.Remove(cert).
|
||||
// This private key matches the certificate generated by the CertFile() method.
|
||||
func (f *Factory) PrivateKey() (string, error) {
|
||||
key := "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgDxCVpEGPQML6jJUczzrbTWxzbT+/fMxDGyPejdR3KVihRANCAAQ00lBf+fgqO8+DW1eVIu124YhYsUHHMkyjmB25OLCsjKsBPVDUQQeSC4d5xEMYWkrMHfmMcbNTN/d5x2+MZGVv"
|
||||
decodedKey, err := base64.StdEncoding.DecodeString(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
keyFile, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(keyFile.Name(), decodedKey, 0); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return keyFile.Name(), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package injector
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
patchPathContainer = "/spec/template/spec/containers/0"
|
||||
patchPathInitContainerRoot = "/spec/template/spec/initContainers"
|
||||
patchPathInitContainer = "/spec/template/spec/initContainers/0"
|
||||
patchPathVolumeRoot = "/spec/template/spec/volumes"
|
||||
patchPathVolume = "/spec/template/spec/volumes/0"
|
||||
patchPathPodLabel = "/spec/template/metadata/labels"
|
||||
patchPathPodAnnotation = "/spec/template/metadata/annotations"
|
||||
)
|
||||
|
||||
// Patch represents a RFC 6902 patch document.
|
||||
type Patch struct {
|
||||
patchOps []*patchOp
|
||||
}
|
||||
|
||||
// NewPatch returns a new instance of PodPatch.
|
||||
func NewPatch() *Patch {
|
||||
return &Patch{
|
||||
patchOps: []*patchOp{},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Patch) addContainer(container *corev1.Container) {
|
||||
p.patchOps = append(p.patchOps, &patchOp{
|
||||
Op: "add",
|
||||
Path: patchPathContainer,
|
||||
Value: container,
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Patch) addInitContainerRoot() {
|
||||
p.patchOps = append(p.patchOps, &patchOp{
|
||||
Op: "add",
|
||||
Path: patchPathInitContainerRoot,
|
||||
Value: []*corev1.Container{},
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Patch) addInitContainer(container *corev1.Container) {
|
||||
p.patchOps = append(p.patchOps, &patchOp{
|
||||
Op: "add",
|
||||
Path: patchPathInitContainer,
|
||||
Value: container,
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Patch) addVolumeRoot() {
|
||||
p.patchOps = append(p.patchOps, &patchOp{
|
||||
Op: "add",
|
||||
Path: patchPathVolumeRoot,
|
||||
Value: []*corev1.Volume{},
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Patch) addVolume(volume *corev1.Volume) {
|
||||
p.patchOps = append(p.patchOps, &patchOp{
|
||||
Op: "add",
|
||||
Path: patchPathVolume,
|
||||
Value: volume,
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Patch) addPodLabel(label map[string]string) {
|
||||
p.patchOps = append(p.patchOps, &patchOp{
|
||||
Op: "add",
|
||||
Path: patchPathPodLabel,
|
||||
Value: label,
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Patch) addPodAnnotation(annotation map[string]string) {
|
||||
p.patchOps = append(p.patchOps, &patchOp{
|
||||
Op: "add",
|
||||
Path: patchPathPodAnnotation,
|
||||
Value: annotation,
|
||||
})
|
||||
}
|
||||
|
||||
// patchOp represents a RFC 6902 patch operation.
|
||||
type patchOp struct {
|
||||
Op string `json:"op"`
|
||||
Path string `json:"path"`
|
||||
Value interface{} `json:"value,omitempty"`
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package injector
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/linkerd/linkerd2/controller/proxy-injector/fake"
|
||||
k8sPkg "github.com/linkerd/linkerd2/pkg/k8s"
|
||||
"k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestPatch(t *testing.T) {
|
||||
fixture := fake.NewFactory()
|
||||
|
||||
trustAnchors, err := fixture.Volume("inject-trust-anchors-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 {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
init, err := fixture.Container("inject-init-container-spec.yaml")
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
var (
|
||||
controllerNamespace = "linkerd"
|
||||
createdBy = "linkerd/cli v18.8.4"
|
||||
)
|
||||
|
||||
actual := NewPatch()
|
||||
actual.addContainer(sidecar)
|
||||
actual.addInitContainerRoot()
|
||||
actual.addInitContainer(init)
|
||||
actual.addVolumeRoot()
|
||||
actual.addVolume(trustAnchors)
|
||||
actual.addVolume(secrets)
|
||||
actual.addPodLabel(map[string]string{
|
||||
k8sPkg.ControllerNSLabel: controllerNamespace,
|
||||
k8sPkg.ProxyAutoInjectLabel: k8sPkg.ProxyAutoInjectCompleted,
|
||||
})
|
||||
actual.addPodAnnotation(map[string]string{
|
||||
k8sPkg.CreatedByAnnotation: createdBy,
|
||||
})
|
||||
|
||||
expected := NewPatch()
|
||||
expected.patchOps = []*patchOp{
|
||||
&patchOp{Op: "add", Path: patchPathContainer, Value: sidecar},
|
||||
&patchOp{Op: "add", Path: patchPathInitContainerRoot, Value: []*v1.Container{}},
|
||||
&patchOp{Op: "add", Path: patchPathInitContainer, Value: init},
|
||||
&patchOp{Op: "add", Path: patchPathVolumeRoot, Value: []*v1.Volume{}},
|
||||
&patchOp{Op: "add", Path: patchPathVolume, Value: trustAnchors},
|
||||
&patchOp{Op: "add", Path: patchPathVolume, Value: secrets},
|
||||
&patchOp{Op: "add", Path: patchPathPodLabel, Value: map[string]string{
|
||||
k8sPkg.ControllerNSLabel: controllerNamespace,
|
||||
k8sPkg.ProxyAutoInjectLabel: k8sPkg.ProxyAutoInjectCompleted,
|
||||
}},
|
||||
&patchOp{Op: "add", Path: patchPathPodAnnotation, Value: map[string]string{k8sPkg.CreatedByAnnotation: createdBy}},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("Content mismatch\nExpected: %s\nActual: %s", expected, actual)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
package injector
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
pem "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
|
||||
// mutate all the requests.
|
||||
type WebhookServer struct {
|
||||
*http.Server
|
||||
*Webhook
|
||||
}
|
||||
|
||||
// NewWebhookServer returns a new instance of the WebhookServer.
|
||||
func NewWebhookServer(client kubernetes.Interface, resources *WebhookResources, addr, controllerNamespace, certFile, keyFile string) (*WebhookServer, error) {
|
||||
c, err := tlsConfig(certFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
server := &http.Server{
|
||||
Addr: addr,
|
||||
TLSConfig: c,
|
||||
}
|
||||
|
||||
webhook, err := NewWebhook(client, resources, controllerNamespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ws := &WebhookServer{server, webhook}
|
||||
ws.Handler = http.HandlerFunc(ws.serve)
|
||||
return ws, nil
|
||||
}
|
||||
|
||||
func (w *WebhookServer) serve(res http.ResponseWriter, req *http.Request) {
|
||||
var (
|
||||
data []byte
|
||||
err error
|
||||
)
|
||||
if req.Body != nil {
|
||||
data, err = ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
http.Error(res, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
response := w.Mutate(data)
|
||||
responseJSON, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
http.Error(res, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := res.Write(responseJSON); err != nil {
|
||||
http.Error(res, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown initiates a graceful shutdown of the underlying HTTP server.
|
||||
func (w *WebhookServer) Shutdown() error {
|
||||
return w.Server.Shutdown(context.Background())
|
||||
}
|
||||
|
||||
func tlsConfig(certFile, keyFile string) (*tls.Config, error) {
|
||||
certBytes, err := ioutil.ReadFile(certFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyBytes, err := ioutil.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certPEM, err := pem.PEMEncodeCert(certBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("PEM-encoded certificate: %s\n", certPEM)
|
||||
|
||||
keyPEM, err := pem.PEMEncodeKey(keyBytes, pem.KeyTypeECDSA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package injector
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/linkerd/linkerd2/controller/proxy-injector/fake"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
testServer *WebhookServer
|
||||
testWebhookResources *WebhookResources
|
||||
)
|
||||
|
||||
func init() {
|
||||
// create a webhook which uses its fake client to seed the sidecar configmap
|
||||
fakeClient, err := fake.NewClient("")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
testWebhookResources = &WebhookResources{
|
||||
FileProxySpec: fake.FileProxySpec,
|
||||
FileProxyInitSpec: fake.FileProxyInitSpec,
|
||||
FileTLSTrustAnchorVolumeSpec: fake.FileTLSTrustAnchorVolumeSpec,
|
||||
FileTLSIdentityVolumeSpec: fake.FileTLSIdentityVolumeSpec,
|
||||
}
|
||||
webhook, err = NewWebhook(fakeClient, testWebhookResources, fake.DefaultControllerNamespace)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.SetOutput(ioutil.Discard)
|
||||
factory = fake.NewFactory()
|
||||
|
||||
factory = fake.NewFactory()
|
||||
testServer = &WebhookServer{nil, webhook}
|
||||
}
|
||||
|
||||
func TestServe(t *testing.T) {
|
||||
t.Run("with empty http request body", func(t *testing.T) {
|
||||
in := bytes.NewReader(nil)
|
||||
request := httptest.NewRequest(http.MethodGet, "/", in)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
testServer.serve(recorder, request)
|
||||
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Errorf("HTTP response status mismatch. Expected: %d. Actual: %d", http.StatusOK, recorder.Code)
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(recorder.Body.Bytes(), []byte("")) {
|
||||
t.Errorf("Content mismatch. Expected HTTP response body to be empty %v", recorder.Body.Bytes())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestShutdown(t *testing.T) {
|
||||
server := &http.Server{Addr: ":0"}
|
||||
testServer := WebhookServer{server, nil}
|
||||
|
||||
go func() {
|
||||
if err := testServer.ListenAndServe(); err != nil {
|
||||
if err != http.ErrServerClosed {
|
||||
t.Errorf("Expected server to be gracefully shutdown with error: %q", http.ErrServerClosed)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := testServer.Shutdown(); err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewWebhookServer(t *testing.T) {
|
||||
certFile, err := factory.CertFile()
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
defer os.Remove(certFile)
|
||||
|
||||
keyFile, err := factory.PrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
defer os.Remove(keyFile)
|
||||
|
||||
var (
|
||||
addr = ":7070"
|
||||
kubeconfig = ""
|
||||
)
|
||||
fakeClient, err := fake.NewClient(kubeconfig)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
server, err := NewWebhookServer(fakeClient, testWebhookResources, addr, fake.DefaultControllerNamespace, certFile, keyFile)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
if server.Addr != fmt.Sprintf("%s", addr) {
|
||||
t.Errorf("Expected server address to be :%q", addr)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package tmpl
|
||||
|
||||
var MutatingWebhookConfigurationSpec = `
|
||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
||||
kind: MutatingWebhookConfiguration
|
||||
metadata:
|
||||
name: {{ .WebhookConfigName }}
|
||||
webhooks:
|
||||
- name: {{ .WebhookServiceName }}
|
||||
clientConfig:
|
||||
service:
|
||||
name: proxy-injector
|
||||
namespace: {{ .ControllerNamespace }}
|
||||
path: "/"
|
||||
caBundle: {{ .CABundle }}
|
||||
rules:
|
||||
- operations: [ "CREATE" ]
|
||||
apiGroups: ["apps", "extensions"]
|
||||
apiVersions: ["v1", "v1beta1", "v1beta2"]
|
||||
resources: ["deployments"]
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: {{.ProxyAutoInjectLabel}}
|
||||
operator: NotIn
|
||||
values:
|
||||
- "disabled"`
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
package injector
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
yaml "github.com/ghodss/yaml"
|
||||
"github.com/linkerd/linkerd2/pkg/healthcheck"
|
||||
k8sPkg "github.com/linkerd/linkerd2/pkg/k8s"
|
||||
log "github.com/sirupsen/logrus"
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "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"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultNamespace = "default"
|
||||
envVarKeyProxyTLSPodIdentity = "LINKERD2_PROXY_TLS_POD_IDENTITY"
|
||||
envVarKeyProxyTLSControllerIdentity = "LINKERD2_PROXY_TLS_CONTROLLER_IDENTITY"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
deserializer runtime.Decoder
|
||||
controllerNamespace string
|
||||
resources *WebhookResources
|
||||
}
|
||||
|
||||
// NewWebhook returns a new instance of Webhook.
|
||||
func NewWebhook(client kubernetes.Interface, resources *WebhookResources, controllerNamespace string) (*Webhook, error) {
|
||||
var (
|
||||
scheme = runtime.NewScheme()
|
||||
codecs = serializer.NewCodecFactory(scheme)
|
||||
)
|
||||
|
||||
return &Webhook{
|
||||
deserializer: codecs.UniversalDeserializer(),
|
||||
controllerNamespace: controllerNamespace,
|
||||
resources: resources,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Mutate changes the given pod spec by injecting the proxy sidecar container
|
||||
// into the spec. The admission review object returns contains the original
|
||||
// request and the response with the mutated pod spec.
|
||||
func (w *Webhook) Mutate(data []byte) *admissionv1beta1.AdmissionReview {
|
||||
admissionReview, err := w.decode(data)
|
||||
if err != nil {
|
||||
log.Error("failed to decode data. Reason: ", err)
|
||||
admissionReview.Response = &admissionv1beta1.AdmissionResponse{
|
||||
UID: admissionReview.Request.UID,
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Message: err.Error(),
|
||||
},
|
||||
}
|
||||
return admissionReview
|
||||
}
|
||||
log.Infof("received admission review request %s", admissionReview.Request.UID)
|
||||
log.Debugf("admission request: %+v", admissionReview.Request)
|
||||
|
||||
admissionResponse, err := w.inject(admissionReview.Request)
|
||||
if err != nil {
|
||||
log.Error("failed to inject sidecar. Reason: ", err)
|
||||
admissionReview.Response = &admissionv1beta1.AdmissionResponse{
|
||||
UID: admissionReview.Request.UID,
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Message: err.Error(),
|
||||
},
|
||||
}
|
||||
return admissionReview
|
||||
}
|
||||
admissionReview.Response = admissionResponse
|
||||
|
||||
if len(admissionResponse.Patch) > 0 {
|
||||
log.Infof("patch generated: %s", admissionResponse.Patch)
|
||||
}
|
||||
log.Info("done")
|
||||
|
||||
return admissionReview
|
||||
}
|
||||
|
||||
func (w *Webhook) decode(data []byte) (*admissionv1beta1.AdmissionReview, error) {
|
||||
var admissionReview admissionv1beta1.AdmissionReview
|
||||
err := yaml.Unmarshal(data, &admissionReview)
|
||||
return &admissionReview, err
|
||||
}
|
||||
|
||||
func (w *Webhook) inject(request *admissionv1beta1.AdmissionRequest) (*admissionv1beta1.AdmissionResponse, error) {
|
||||
var deployment appsv1.Deployment
|
||||
if err := yaml.Unmarshal(request.Object.Raw, &deployment); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Infof("working on %s/%s %s..", request.Kind.Version, strings.ToLower(request.Kind.Kind), deployment.ObjectMeta.Name)
|
||||
|
||||
ns := request.Namespace
|
||||
if ns == "" {
|
||||
ns = defaultNamespace
|
||||
}
|
||||
log.Infof("resource namespace: %s", ns)
|
||||
|
||||
if w.ignore(&deployment) {
|
||||
log.Infof("ignoring deployment %s", deployment.ObjectMeta.Name)
|
||||
return &admissionv1beta1.AdmissionResponse{
|
||||
UID: request.UID,
|
||||
Allowed: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
identity := &k8sPkg.TLSIdentity{
|
||||
Name: deployment.ObjectMeta.Name,
|
||||
Kind: strings.ToLower(request.Kind.Kind),
|
||||
Namespace: ns,
|
||||
ControllerNamespace: w.controllerNamespace,
|
||||
}
|
||||
proxy, proxyInit, err := w.containersSpec(identity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Infof("proxy image: %s", proxy.Image)
|
||||
log.Infof("proxy-init image: %s", proxyInit.Image)
|
||||
log.Debugf("proxy container: %+v", proxy)
|
||||
log.Debugf("init container: %+v", proxyInit)
|
||||
|
||||
caBundle, tlsSecrets, err := w.volumesSpec(identity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("ca bundle volume: %+v", caBundle)
|
||||
log.Debugf("tls secrets volume: %+v", tlsSecrets)
|
||||
|
||||
patch := NewPatch()
|
||||
patch.addContainer(proxy)
|
||||
|
||||
if len(deployment.Spec.Template.Spec.InitContainers) == 0 {
|
||||
patch.addInitContainerRoot()
|
||||
}
|
||||
patch.addInitContainer(proxyInit)
|
||||
|
||||
if len(deployment.Spec.Template.Spec.Volumes) == 0 {
|
||||
patch.addVolumeRoot()
|
||||
}
|
||||
patch.addVolume(caBundle)
|
||||
patch.addVolume(tlsSecrets)
|
||||
|
||||
patch.addPodLabel(map[string]string{
|
||||
k8sPkg.ControllerNSLabel: w.controllerNamespace,
|
||||
k8sPkg.ProxyDeploymentLabel: deployment.ObjectMeta.Name,
|
||||
})
|
||||
|
||||
var (
|
||||
image = strings.Split(proxy.Image, ":")
|
||||
imageTag = ""
|
||||
)
|
||||
|
||||
if len(image) < 2 {
|
||||
imageTag = "latest"
|
||||
} else {
|
||||
imageTag = image[1]
|
||||
}
|
||||
patch.addPodAnnotation(map[string]string{
|
||||
k8sPkg.CreatedByAnnotation: fmt.Sprintf("linkerd/proxy-injector %s", imageTag),
|
||||
k8sPkg.ProxyVersionAnnotation: imageTag,
|
||||
})
|
||||
|
||||
patchJSON, err := json.Marshal(patch.patchOps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patchType := admissionv1beta1.PatchTypeJSONPatch
|
||||
admissionResponse := &admissionv1beta1.AdmissionResponse{
|
||||
UID: request.UID,
|
||||
Allowed: true,
|
||||
Patch: patchJSON,
|
||||
PatchType: &patchType,
|
||||
}
|
||||
|
||||
return admissionResponse, nil
|
||||
}
|
||||
|
||||
func (w *Webhook) ignore(deployment *appsv1.Deployment) bool {
|
||||
labels := deployment.Spec.Template.ObjectMeta.GetLabels()
|
||||
status, defined := labels[k8sPkg.ProxyAutoInjectLabel]
|
||||
if defined {
|
||||
switch status {
|
||||
case k8sPkg.ProxyAutoInjectDisabled, k8sPkg.ProxyAutoInjectCompleted:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return healthcheck.HasExistingSidecars(&deployment.Spec.Template.Spec)
|
||||
}
|
||||
|
||||
func (w *Webhook) containersSpec(identity *k8sPkg.TLSIdentity) (*corev1.Container, *corev1.Container, error) {
|
||||
proxySpec, err := ioutil.ReadFile(w.resources.FileProxySpec)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var proxy corev1.Container
|
||||
if err := yaml.Unmarshal(proxySpec, &proxy); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for index, env := range proxy.Env {
|
||||
if env.Name == envVarKeyProxyTLSPodIdentity {
|
||||
proxy.Env[index].Value = identity.ToDNSName()
|
||||
} else if env.Name == envVarKeyProxyTLSControllerIdentity {
|
||||
proxy.Env[index].Value = identity.ToControllerIdentity().ToDNSName()
|
||||
}
|
||||
}
|
||||
|
||||
proxyInitSpec, err := ioutil.ReadFile(w.resources.FileProxyInitSpec)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var proxyInit corev1.Container
|
||||
if err := yaml.Unmarshal(proxyInitSpec, &proxyInit); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &proxy, &proxyInit, nil
|
||||
}
|
||||
|
||||
func (w *Webhook) volumesSpec(identity *k8sPkg.TLSIdentity) (*corev1.Volume, *corev1.Volume, error) {
|
||||
trustAnchorVolumeSpec, err := ioutil.ReadFile(w.resources.FileTLSTrustAnchorVolumeSpec)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var trustAnchors corev1.Volume
|
||||
if err := yaml.Unmarshal(trustAnchorVolumeSpec, &trustAnchors); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
tlsVolumeSpec, err := ioutil.ReadFile(w.resources.FileTLSIdentityVolumeSpec)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var linkerdSecrets corev1.Volume
|
||||
if err := yaml.Unmarshal(tlsVolumeSpec, &linkerdSecrets); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
linkerdSecrets.VolumeSource.Secret.SecretName = identity.ToSecretName()
|
||||
|
||||
return &trustAnchors, &linkerdSecrets, nil
|
||||
}
|
||||
|
||||
// WebhookResources contain paths to all the needed file resources.
|
||||
type WebhookResources struct {
|
||||
// FileProxySpec is the path to the proxy spec.
|
||||
FileProxySpec string
|
||||
|
||||
// FileProxyInitSpec is the path to the proxy-init spec.
|
||||
FileProxyInitSpec string
|
||||
|
||||
// FileTLSTrustAnchorVolumeSpec is the path to the trust anchor volume spec.
|
||||
FileTLSTrustAnchorVolumeSpec string
|
||||
|
||||
// FileTLSIdentityVolumeSpec is the path to the TLS identity volume spec.
|
||||
FileTLSIdentityVolumeSpec string
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
package injector
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"io/ioutil"
|
||||
"text/template"
|
||||
|
||||
yaml "github.com/ghodss/yaml"
|
||||
"github.com/linkerd/linkerd2/controller/proxy-injector/tmpl"
|
||||
k8sPkg "github.com/linkerd/linkerd2/pkg/k8s"
|
||||
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"
|
||||
)
|
||||
|
||||
// WebhookConfig creates the MutatingWebhookConfiguration of the webhook.
|
||||
type WebhookConfig struct {
|
||||
controllerNamespace string
|
||||
webhookServiceName string
|
||||
trustAnchor []byte
|
||||
configTemplate *template.Template
|
||||
k8sAPI kubernetes.Interface
|
||||
}
|
||||
|
||||
// NewWebhookConfig returns a new instance of initiator.
|
||||
func NewWebhookConfig(client kubernetes.Interface, controllerNamespace, webhookServiceName, trustAnchorFile string) (*WebhookConfig, error) {
|
||||
trustAnchor, err := ioutil.ReadFile(trustAnchorFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := template.New(k8sPkg.ProxyInjectorWebhookConfig)
|
||||
|
||||
return &WebhookConfig{
|
||||
controllerNamespace: controllerNamespace,
|
||||
webhookServiceName: webhookServiceName,
|
||||
trustAnchor: trustAnchor,
|
||||
configTemplate: template.Must(t.Parse(tmpl.MutatingWebhookConfigurationSpec)),
|
||||
k8sAPI: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateOrUpdate sends the request to either create or update the
|
||||
// MutatingWebhookConfiguration resource. During an update, only the CA bundle
|
||||
// is changed.
|
||||
func (w *WebhookConfig) CreateOrUpdate() (*arv1beta1.MutatingWebhookConfiguration, error) {
|
||||
mwc, exist, err := w.exist()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !exist {
|
||||
return w.create()
|
||||
}
|
||||
|
||||
return w.update(mwc)
|
||||
}
|
||||
|
||||
// exist returns true if the mutating webhook configuration exists. Otherwise,
|
||||
// it returns false.
|
||||
func (w *WebhookConfig) exist() (*arv1beta1.MutatingWebhookConfiguration, bool, error) {
|
||||
mwc, err := w.k8sAPI.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Get(k8sPkg.ProxyInjectorWebhookConfig, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return mwc, true, nil
|
||||
}
|
||||
|
||||
func (w *WebhookConfig) create() (*arv1beta1.MutatingWebhookConfiguration, error) {
|
||||
var (
|
||||
buf = &bytes.Buffer{}
|
||||
spec = struct {
|
||||
WebhookConfigName string
|
||||
WebhookServiceName string
|
||||
ControllerNamespace string
|
||||
CABundle string
|
||||
ProxyAutoInjectLabel string
|
||||
}{
|
||||
WebhookConfigName: k8sPkg.ProxyInjectorWebhookConfig,
|
||||
WebhookServiceName: w.webhookServiceName,
|
||||
ControllerNamespace: w.controllerNamespace,
|
||||
CABundle: base64.StdEncoding.EncodeToString(w.trustAnchor),
|
||||
ProxyAutoInjectLabel: k8sPkg.ProxyAutoInjectLabel,
|
||||
}
|
||||
)
|
||||
if err := w.configTemplate.Execute(buf, spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var config arv1beta1.MutatingWebhookConfiguration
|
||||
if err := yaml.Unmarshal(buf.Bytes(), &config); err != nil {
|
||||
log.Infof("failed to unmarshal mutating webhook configuration: %s\n%s\n", err, buf.String())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return w.k8sAPI.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&config)
|
||||
}
|
||||
|
||||
func (w *WebhookConfig) update(mwc *arv1beta1.MutatingWebhookConfiguration) (*arv1beta1.MutatingWebhookConfiguration, error) {
|
||||
for i := 0; i < len(mwc.Webhooks); i++ {
|
||||
mwc.Webhooks[i].ClientConfig.CABundle = w.trustAnchor
|
||||
}
|
||||
|
||||
return w.k8sAPI.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Update(mwc)
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package injector
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/linkerd/linkerd2/controller/proxy-injector/fake"
|
||||
)
|
||||
|
||||
func TestCreateOrUpdate(t *testing.T) {
|
||||
var (
|
||||
factory = fake.NewFactory()
|
||||
namespace = fake.DefaultControllerNamespace
|
||||
webhookServiceName = "test.linkerd.io"
|
||||
)
|
||||
log.SetOutput(ioutil.Discard)
|
||||
|
||||
client, err := fake.NewClient("")
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
trustAnchorsPath, err := factory.CATrustAnchors()
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
defer os.Remove(trustAnchorsPath)
|
||||
|
||||
webhookConfig, err := NewWebhookConfig(client, namespace, webhookServiceName, trustAnchorsPath)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
// expect mutating webhook configuration to not exist
|
||||
_, exist, err := webhookConfig.exist()
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
if exist {
|
||||
t.Error("Unexpected mutating webhook configuration. Expect resources to not exist")
|
||||
}
|
||||
|
||||
// create the mutating webhook configuration
|
||||
if _, err := webhookConfig.CreateOrUpdate(); err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
// expect mutating webhook configuration to exist
|
||||
_, exist, err = webhookConfig.exist()
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
if !exist {
|
||||
t.Error("Expected mutating webhook configuration to exist")
|
||||
}
|
||||
|
||||
// update the mutating webhook configuration using the same trust anchors
|
||||
if _, err := webhookConfig.CreateOrUpdate(); err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
package injector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/linkerd/linkerd2/controller/proxy-injector/fake"
|
||||
"github.com/linkerd/linkerd2/pkg/k8s"
|
||||
log "github.com/sirupsen/logrus"
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
)
|
||||
|
||||
var (
|
||||
webhook *Webhook
|
||||
factory *fake.Factory
|
||||
)
|
||||
|
||||
func init() {
|
||||
// create a webhook which uses its fake client to seed the sidecar configmap
|
||||
fakeClient, err := fake.NewClient("")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
webhook, err = NewWebhook(fakeClient, testWebhookResources, fake.DefaultControllerNamespace)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.SetOutput(ioutil.Discard)
|
||||
}
|
||||
|
||||
func TestMutate(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
title string
|
||||
requestFile string
|
||||
responseFile string
|
||||
}{
|
||||
{title: "no labels", requestFile: "inject-no-labels-request.json", responseFile: "inject-no-labels-response.yaml"},
|
||||
{title: "inject enabled", requestFile: "inject-enabled-request.json", responseFile: "inject-enabled-response.yaml"},
|
||||
{title: "inject disabled", requestFile: "inject-disabled-request.json", responseFile: "inject-disabled-response.yaml"},
|
||||
{title: "inject completed", requestFile: "inject-completed-request.json", responseFile: "inject-completed-response.yaml"},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(fmt.Sprintf("%s", testCase.title), func(t *testing.T) {
|
||||
data, err := factory.HTTPRequestBody(testCase.requestFile)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
expected, err := factory.AdmissionReview(testCase.responseFile)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
actual := webhook.Mutate(data)
|
||||
assertEqualAdmissionReview(t, expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIgnore(t *testing.T) {
|
||||
t.Run("by checking labels", func(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
filename string
|
||||
expected bool
|
||||
}{
|
||||
{filename: "deployment-inject-status-empty.yaml", expected: false},
|
||||
{filename: "deployment-inject-status-enabled.yaml", expected: false},
|
||||
{filename: "deployment-inject-status-disabled.yaml", expected: true},
|
||||
{filename: "deployment-inject-status-completed.yaml", expected: true},
|
||||
}
|
||||
|
||||
for id, testCase := range testCases {
|
||||
t.Run(fmt.Sprintf("%d", id), func(t *testing.T) {
|
||||
deployment, err := factory.Deployment(testCase.filename)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
if actual := webhook.ignore(deployment); actual != testCase.expected {
|
||||
t.Errorf("Boolean mismatch. Expected: %t. Actual: %t", testCase.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("by checking container spec", func(t *testing.T) {
|
||||
deployment, err := factory.Deployment("deployment-with-injected-proxy.yaml")
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
if !webhook.ignore(deployment) {
|
||||
t.Errorf("Expected deployment with injected proxy to be ignored")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestContainersSpec(t *testing.T) {
|
||||
expectedSidecar, err := factory.Container("inject-sidecar-container-spec.yaml")
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
expectedInit, err := factory.Container("inject-init-container-spec.yaml")
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
identity := &k8s.TLSIdentity{
|
||||
Name: "nginx",
|
||||
Kind: "deployment",
|
||||
Namespace: fake.DefaultNamespace,
|
||||
ControllerNamespace: fake.DefaultControllerNamespace,
|
||||
}
|
||||
|
||||
actualSidecar, actualInit, err := webhook.containersSpec(identity)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedSidecar, actualSidecar) {
|
||||
t.Errorf("Content mismatch\nExpected: %+v\nActual: %+v", expectedSidecar, actualSidecar)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedInit, actualInit) {
|
||||
t.Errorf("Content mismatch\nExpected: %+v\nActual: %+v", expectedInit, actualInit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolumesSpec(t *testing.T) {
|
||||
expectedTrustAnchors, err := factory.Volume("inject-trust-anchors-volume-spec.yaml")
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
expectedLinkerdSecrets, err := factory.Volume("inject-linkerd-secrets-volume-spec.yaml")
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
identity := &k8s.TLSIdentity{
|
||||
Name: "nginx",
|
||||
Kind: "deployment",
|
||||
Namespace: fake.DefaultNamespace,
|
||||
ControllerNamespace: fake.DefaultControllerNamespace,
|
||||
}
|
||||
|
||||
actualTrustAnchors, actualLinkerdSecrets, err := webhook.volumesSpec(identity)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedTrustAnchors, actualTrustAnchors) {
|
||||
t.Errorf("Content mismatch\nExpected: %+v\nActual: %+v", expectedTrustAnchors, actualTrustAnchors)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedLinkerdSecrets, actualLinkerdSecrets) {
|
||||
t.Errorf("Content mismatch\nExpected: %+v\nActual: %+v", expectedLinkerdSecrets, actualLinkerdSecrets)
|
||||
}
|
||||
}
|
||||
|
||||
func assertEqualAdmissionReview(t *testing.T, expected, actual *admissionv1beta1.AdmissionReview) {
|
||||
if !reflect.DeepEqual(expected.Request, actual.Request) {
|
||||
if !reflect.DeepEqual(expected.Request.Object, actual.Request.Object) {
|
||||
t.Errorf("Request object mismatch\nExpected: %s\nActual: %s", expected.Request.Object, actual.Request.Object)
|
||||
} else {
|
||||
t.Errorf("Request mismatch\nExpected: %+v\nActual: %+v", expected.Request, actual.Request)
|
||||
}
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Response, actual.Response) {
|
||||
if actual.Response.Result != nil {
|
||||
t.Errorf("Actual response message: %s", actual.Response.Result.Message)
|
||||
}
|
||||
t.Errorf("Response patch mismatch\nExpected: %s\nActual: %s", expected.Response.Patch, actual.Response.Patch)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package healthcheck
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// HasExistingSidecars returns true if the pod spec already has the proxy init
|
||||
// and sidecar cntainers injected. Otherwise, it returns false.
|
||||
func HasExistingSidecars(podSpec *corev1.PodSpec) bool {
|
||||
for _, container := range podSpec.Containers {
|
||||
if strings.HasPrefix(container.Image, "gcr.io/linkerd-io/proxy:") ||
|
||||
strings.HasPrefix(container.Image, "gcr.io/istio-release/proxyv2:") ||
|
||||
strings.HasPrefix(container.Image, "gcr.io/heptio-images/contour:") ||
|
||||
strings.HasPrefix(container.Image, "docker.io/envoyproxy/envoy-alpine:") ||
|
||||
container.Name == "linkerd-proxy" ||
|
||||
container.Name == "istio-proxy" ||
|
||||
container.Name == "contour" ||
|
||||
container.Name == "envoy" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, ic := range podSpec.InitContainers {
|
||||
if strings.HasPrefix(ic.Image, "gcr.io/linkerd-io/proxy-init:") ||
|
||||
strings.HasPrefix(ic.Image, "gcr.io/istio-release/proxy_init:") ||
|
||||
strings.HasPrefix(ic.Image, "gcr.io/heptio-images/contour:") ||
|
||||
ic.Name == "linkerd-init" ||
|
||||
ic.Name == "istio-init" ||
|
||||
ic.Name == "envoy-initconfig" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
@ -62,6 +62,22 @@ const (
|
|||
// (e.g. v0.1.3).
|
||||
ProxyVersionAnnotation = "linkerd.io/proxy-version"
|
||||
|
||||
// ProxyAutoInjectLabel indicates if sidecar auto-inject should be performed
|
||||
// on the pod. Supported values are "enabled", "disabled" or "completed".
|
||||
ProxyAutoInjectLabel = "linkerd.io/auto-inject"
|
||||
|
||||
// ProxyAutoInjectEnabled is assigned to the ProxyAutoInjectLabel label to
|
||||
// indicate that the sidecar auto-inject is enabled for a particular resource.
|
||||
ProxyAutoInjectEnabled = "enabled"
|
||||
|
||||
// ProxyAutoInjectDisabled is assigned to the ProxyAutoInjectLabel label to
|
||||
// indicate that the sidecar auto-inject is disabled for a particular resource.
|
||||
ProxyAutoInjectDisabled = "disabled"
|
||||
|
||||
// ProxyAutoInjectDisabled is assigned to the ProxyAutoInjectLabel label to
|
||||
// indicate that the sidecar auto-inject is completed for a particular resource.
|
||||
ProxyAutoInjectCompleted = "completed"
|
||||
|
||||
/*
|
||||
* Component Names
|
||||
*/
|
||||
|
|
@ -72,6 +88,35 @@ const (
|
|||
// ProxyContainerName is the name assigned to the injected proxy container.
|
||||
ProxyContainerName = "linkerd-proxy"
|
||||
|
||||
// ProxyInjectorTLSSecret is the name assigned to the secret containing the
|
||||
// TLS cert and key used by the proxy-injector webhook.
|
||||
ProxyInjectorTLSSecret = "proxy-injector-service-tls-linkerd-io"
|
||||
|
||||
// ProxyInjectorWebhookConfig is the name of the mutating webhook
|
||||
// configuration resource of the proxy-injector webhook.
|
||||
ProxyInjectorWebhookConfig = "linkerd-proxy-injector-webhook-config"
|
||||
|
||||
// ProxyInjectorSidecarConfig is the name of the config map resource that
|
||||
// contains the specs of the proxy init container and sidecar container to be
|
||||
// injected into a pod.
|
||||
ProxyInjectorSidecarConfig = "proxy-injector-sidecar-config"
|
||||
|
||||
// ProxySpecFileName is the name (key) within the proxy-injector ConfigMap
|
||||
// that contains the proxy container spec.
|
||||
ProxySpecFileName = "proxy.yaml"
|
||||
|
||||
// ProxyInjectorInitContainerSpecFileName is the name (key) within the
|
||||
// proxy-injector ConfigMap that contains the proxy-init container spec.
|
||||
ProxyInitSpecFileName = "proxy-init.yaml"
|
||||
|
||||
// TLSTrustAnchorVolumeSpecFileName is the name (key) within the
|
||||
// proxy-injector ConfigMap that contains the trust anchors volume spec.
|
||||
TLSTrustAnchorVolumeSpecFileName = "linkerd-trust-anchors.yaml"
|
||||
|
||||
// TLSIdentityVolumeSpecFileName is the name (key) within the
|
||||
// proxy-injector ConfigMap that contains the TLS identity secrets volume spec.
|
||||
TLSIdentityVolumeSpecFileName = "linkerd-secrets.yaml"
|
||||
|
||||
// TLSTrustAnchorConfigMapName is the name of the ConfigMap that holds the
|
||||
// trust anchors (trusted root certificates).
|
||||
TLSTrustAnchorConfigMapName = "linkerd-ca-bundle"
|
||||
|
|
@ -82,6 +127,43 @@ const (
|
|||
|
||||
TLSCertFileName = "certificate.crt"
|
||||
TLSPrivateKeyFileName = "private-key.p8"
|
||||
|
||||
/*
|
||||
* Mount paths
|
||||
*/
|
||||
|
||||
// MountPathBase is the base directory of the mount path
|
||||
MountPathBase = "/var/linkerd-io"
|
||||
)
|
||||
|
||||
var (
|
||||
// MountPathTLSTrustAnchor is the path at which the trust anchor file is
|
||||
// mounted
|
||||
MountPathTLSTrustAnchor = MountPathBase + "/trust-anchors/" + TLSTrustAnchorFileName
|
||||
|
||||
// MountPathTLSIdentityCert is the path at which the TLS identity cert file is
|
||||
// mounted
|
||||
MountPathTLSIdentityCert = MountPathBase + "/identity/" + TLSCertFileName
|
||||
|
||||
// MountPathTLSIdentityKey is the path at which the TLS identity key file is
|
||||
// mounted
|
||||
MountPathTLSIdentityKey = MountPathBase + "/identity/" + TLSPrivateKeyFileName
|
||||
|
||||
// MountPathConfigProxySpec is the path at which the proxy container spec is
|
||||
// mounted to the proxy-injector
|
||||
MountPathConfigProxySpec = MountPathBase + "/config/" + ProxySpecFileName
|
||||
|
||||
// MountPathConfigProxyInitSpec is the path at which the proxy-init container
|
||||
// spec is mounted to the proxy-injector
|
||||
MountPathConfigProxyInitSpec = MountPathBase + "/config/" + ProxyInitSpecFileName
|
||||
|
||||
// MountTLSPathTrustAnchorVolumeSpec is the path at which the trust anchor
|
||||
// volume spec is mounted to the proxy-injector
|
||||
MountPathTLSTrustAnchorVolumeSpec = MountPathBase + "/config/" + TLSTrustAnchorVolumeSpecFileName
|
||||
|
||||
// MountPathTLSIdentityVolumeSpec is the path at which the TLS identity
|
||||
// secret volume spec is mounted to the proxy-injector
|
||||
MountPathTLSIdentityVolumeSpec = MountPathBase + "/config/" + TLSIdentityVolumeSpecFileName
|
||||
)
|
||||
|
||||
// CreatedByAnnotationValue returns the value associated with
|
||||
|
|
@ -133,6 +215,9 @@ type TLSIdentity struct {
|
|||
}
|
||||
|
||||
func (i TLSIdentity) ToDNSName() string {
|
||||
if i.Kind == Service {
|
||||
return fmt.Sprintf("%s.%s.svc", i.Name, i.Namespace)
|
||||
}
|
||||
return fmt.Sprintf("%s.%s.%s.linkerd-managed.%s.svc.cluster.local", i.Name,
|
||||
i.Kind, i.Namespace, i.ControllerNamespace)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package tls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
KeyTypeRSA = "rsa"
|
||||
KeyTypeECDSA = "ecdsa"
|
||||
)
|
||||
|
||||
// PEMEncodeCertToMemory returns the PEM encoding of cert.
|
||||
func PEMEncodeCert(cert []byte) ([]byte, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
err := pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: cert})
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// PEMEncodeKey returns the PEM encoding of key.
|
||||
func PEMEncodeKey(key []byte, keyType string) ([]byte, error) {
|
||||
pemBlock := &pem.Block{Bytes: key}
|
||||
switch keyType {
|
||||
case KeyTypeRSA:
|
||||
pemBlock.Type = "RSA PRIVATE KEY"
|
||||
case KeyTypeECDSA:
|
||||
pemBlock.Type = "EC PRIVATE KEY"
|
||||
default:
|
||||
return nil, fmt.Errorf("Unknown key type")
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
err := pem.Encode(buf, pemBlock)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
## compile proxy-init utility
|
||||
FROM gcr.io/linkerd-io/go-deps:64a32a2a as golang
|
||||
FROM gcr.io/linkerd-io/go-deps:0c590d0e as golang
|
||||
WORKDIR /go/src/github.com/linkerd/linkerd2
|
||||
COPY ./proxy-init ./proxy-init
|
||||
RUN CGO_ENABLED=0 GOOS=linux go install -v ./proxy-init/
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ ENV NODE_ENV production
|
|||
RUN $ROOT/bin/web build
|
||||
|
||||
## compile go server
|
||||
FROM gcr.io/linkerd-io/go-deps:64a32a2a as golang
|
||||
FROM gcr.io/linkerd-io/go-deps:0c590d0e as golang
|
||||
WORKDIR /go/src/github.com/linkerd/linkerd2
|
||||
COPY web web
|
||||
COPY controller controller
|
||||
|
|
|
|||
Loading…
Reference in New Issue