blob: 7c18ca7aa2c82e9a8071e1291d4c037786f3018f [file] [log] [blame]
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package injection
import (
"encoding/json"
"fmt"
"strings"
)
import (
"istio.io/api/annotation"
"istio.io/api/label"
v1 "k8s.io/api/core/v1"
)
import (
"github.com/apache/dubbo-go-pixiu/pkg/config/analysis"
"github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util"
"github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg"
"github.com/apache/dubbo-go-pixiu/pkg/config/constants"
"github.com/apache/dubbo-go-pixiu/pkg/config/resource"
"github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection"
"github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections"
)
// Analyzer checks conditions related to Istio sidecar injection.
type Analyzer struct{}
var _ analysis.Analyzer = &Analyzer{}
// We assume that enablement is via an istio-injection=enabled or istio.io/rev namespace label
// In theory, there can be alternatives using Mutatingwebhookconfiguration, but they're very uncommon
// See https://istio.io/docs/ops/troubleshooting/injection/ for more info.
var (
RevisionInjectionLabelName = label.IoIstioRev.Name
)
// Metadata implements Analyzer
func (a *Analyzer) Metadata() analysis.Metadata {
return analysis.Metadata{
Name: "injection.Analyzer",
Description: "Checks conditions related to Istio sidecar injection",
Inputs: collection.Names{
collections.K8SCoreV1Namespaces.Name(),
collections.K8SCoreV1Pods.Name(),
collections.K8SCoreV1Configmaps.Name(),
},
}
}
// Analyze implements Analyzer
func (a *Analyzer) Analyze(c analysis.Context) {
enableNamespacesByDefault := false
injectedNamespaces := make(map[string]bool)
c.ForEach(collections.K8SCoreV1Namespaces.Name(), func(r *resource.Instance) bool {
if r.Metadata.FullName.String() == constants.IstioSystemNamespace {
return true
}
ns := r.Metadata.FullName.String()
if util.IsSystemNamespace(resource.Namespace(ns)) {
return true
}
injectionLabel := r.Metadata.Labels[util.InjectionLabelName]
nsRevision, okNewInjectionLabel := r.Metadata.Labels[RevisionInjectionLabelName]
// verify the enableNamespacesByDefault flag in injection configmaps
c.ForEach(collections.K8SCoreV1Configmaps.Name(), func(r *resource.Instance) bool {
injectionCMName := util.GetInjectorConfigMapName(nsRevision)
if r.Metadata.FullName.Name.String() == injectionCMName {
cm := r.Message.(*v1.ConfigMap)
enableNamespacesByDefault = GetEnableNamespacesByDefaultFromInjectedConfigMap(cm)
return false
}
return true
})
if injectionLabel == "" && !okNewInjectionLabel {
// if Istio is installed with sidecarInjectorWebhook.enableNamespacesByDefault=true
// (in the istio-sidecar-injector configmap), we need to reverse this logic and treat this as an injected namespace
if enableNamespacesByDefault {
m := msg.NewNamespaceInjectionEnabledByDefault(r)
c.Report(collections.K8SCoreV1Namespaces.Name(), m)
return true
}
m := msg.NewNamespaceNotInjected(r, ns, ns)
if line, ok := util.ErrorLine(r, fmt.Sprintf(util.MetadataName)); ok {
m.Line = line
}
c.Report(collections.K8SCoreV1Namespaces.Name(), m)
return true
}
if okNewInjectionLabel {
if injectionLabel != "" {
m := msg.NewNamespaceMultipleInjectionLabels(r, ns, ns)
if line, ok := util.ErrorLine(r, fmt.Sprintf(util.MetadataName)); ok {
m.Line = line
}
c.Report(collections.K8SCoreV1Namespaces.Name(), m)
return true
}
} else if injectionLabel != util.InjectionLabelEnableValue {
// If legacy label has any value other than the enablement value, they are deliberately not injecting it, so ignore
return true
}
injectedNamespaces[ns] = true
return true
})
c.ForEach(collections.K8SCoreV1Pods.Name(), func(r *resource.Instance) bool {
pod := r.Message.(*v1.PodSpec)
if !injectedNamespaces[r.Metadata.FullName.Namespace.String()] {
return true
}
// If a pod has injection explicitly disabled, no need to check further
if val := r.Metadata.Annotations[annotation.SidecarInject.Name]; strings.EqualFold(val, "false") {
return true
}
proxyImage := ""
for _, container := range pod.Containers {
if container.Name == util.IstioProxyName {
proxyImage = container.Image
break
}
}
if proxyImage == "" {
c.Report(collections.K8SCoreV1Pods.Name(), msg.NewPodMissingProxy(r, r.Metadata.FullName.String()))
}
return true
})
}
// GetInjectedConfigMapValuesStruct retrieves value of sidecarInjectorWebhook.enableNamespacesByDefault
// defined in the sidecar injector configuration.
func GetEnableNamespacesByDefaultFromInjectedConfigMap(cm *v1.ConfigMap) bool {
var injectedCMValues map[string]interface{}
if err := json.Unmarshal([]byte(cm.Data[util.InjectionConfigMapValue]), &injectedCMValues); err != nil {
return false
}
injectionEnable := injectedCMValues[util.InjectorWebhookConfigKey].(map[string]interface{})[util.InjectorWebhookConfigValue]
return injectionEnable.(bool)
}