Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix double revision in istio configmap name #6675

Merged
merged 3 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions business/istio_certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (ics *IstioCertsService) getCertificateFromSecret(secretName, certName stri
func (ics *IstioCertsService) getCertsConfigFromIstioConfigMap() ([]certConfig, error) {
cfg := config.Get()

istioConfigMap, err := ics.k8s.GetConfigMap(cfg.IstioNamespace, cfg.ExternalServices.Istio.ConfigMapName)
istioConfigMap, err := ics.k8s.GetConfigMap(cfg.IstioNamespace, IstioConfigMapName(*cfg, ""))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -163,7 +163,7 @@ func (ics *IstioCertsService) getChironCertificates(certsConfig []certConfig) ([
func (ics *IstioCertsService) GetTlsMinVersion() (string, error) {
cfg := config.Get()

istioConfigMap, err := ics.k8s.GetConfigMap(cfg.IstioNamespace, cfg.ExternalServices.Istio.ConfigMapName)
istioConfigMap, err := ics.k8s.GetConfigMap(cfg.IstioNamespace, IstioConfigMapName(*cfg, ""))
if err != nil {
return "N/A", err
}
Expand Down
4 changes: 2 additions & 2 deletions business/istio_validations.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,9 +563,9 @@ func (in *IstioValidationsService) fetchNonLocalmTLSConfigs(mtlsDetails *kuberne
var istioConfig *core_v1.ConfigMap
var err error
if IsNamespaceCached(cfg.IstioNamespace) {
istioConfig, err = kialiCache.GetConfigMap(cfg.IstioNamespace, cfg.ExternalServices.Istio.ConfigMapName)
istioConfig, err = kialiCache.GetConfigMap(cfg.IstioNamespace, IstioConfigMapName(*cfg, ""))
} else {
istioConfig, err = userClient.GetConfigMap(cfg.IstioNamespace, cfg.ExternalServices.Istio.ConfigMapName)
istioConfig, err = userClient.GetConfigMap(cfg.IstioNamespace, IstioConfigMapName(*cfg, ""))
}
if err != nil {
errChan <- err
Expand Down
92 changes: 88 additions & 4 deletions business/istio_validations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
networking_v1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1"
security_v1beta "istio.io/client-go/pkg/apis/security/v1beta1"
apps_v1 "k8s.io/api/apps/v1"
core_v1 "k8s.io/api/core/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand Down Expand Up @@ -67,11 +68,92 @@ func TestGatewayValidation(t *testing.T) {
conf := config.NewConfig()
config.Set(conf)

v := mockMultiNamespaceGatewaysValidationService(t)
v := mockMultiNamespaceGatewaysValidationService(t, *conf)
validations, _, _ := v.GetIstioObjectValidations(context.TODO(), conf.KubernetesConfig.ClusterName, "test", "gateways", "first")
assert.NotEmpty(validations)
}

// TestGatewayValidationScopesToNamespaceWhenGatewayToNamespaceSet this test ensures that gateway validation
// scopes the gateway workload checker to the namespace of the gateway when PILOT_SCOPE_GATEWAY_TO_NAMESPACE
// is set to true on the istiod deployment.
func TestGatewayValidationScopesToNamespaceWhenGatewayToNamespaceSet(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
const (
istioConfigMapName = "istio-1-19-0"
istioSidecarInjectorConfigMapName = "istio-sidecar-injector-1-19-0"
istiodDeploymentName = "istiod-1-19-0"
)
conf := config.NewConfig()
conf.ExternalServices.Istio.ConfigMapName = istioConfigMapName
conf.ExternalServices.Istio.IstioSidecarInjectorConfigMapName = istioSidecarInjectorConfigMapName
conf.ExternalServices.Istio.IstiodDeploymentName = istiodDeploymentName
config.Set(conf)
revConfigMap := &core_v1.ConfigMap{ObjectMeta: meta_v1.ObjectMeta{Name: istioConfigMapName, Namespace: "istio-system"}}
injectorConfigMap := &core_v1.ConfigMap{ObjectMeta: meta_v1.ObjectMeta{Name: istioSidecarInjectorConfigMapName, Namespace: "istio-system"}}
istioSystemNamespace := &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "istio-system"}}

istiod_1_19_0 := &apps_v1.Deployment{
ObjectMeta: meta_v1.ObjectMeta{
Name: istiodDeploymentName,
Namespace: "istio-system",
Labels: map[string]string{
IstioRevisionLabel: "1-19-0",
"app": "istiod",
},
},
Spec: apps_v1.DeploymentSpec{
Template: core_v1.PodTemplateSpec{
Spec: core_v1.PodSpec{
Containers: []core_v1.Container{
{
Env: []core_v1.EnvVar{
{
Name: "PILOT_SCOPE_GATEWAY_TO_NAMESPACE",
Value: "true",
},
},
},
},
},
},
},
}

// The gateway workload is in a different namespace than the Gateway object.
gatewayDeployment := &apps_v1.Deployment{
ObjectMeta: meta_v1.ObjectMeta{
Name: "istio-ingressgateway",
Namespace: "istio-system",
Labels: map[string]string{
"app": "real", // Matches the gateway label selector
},
},
Spec: apps_v1.DeploymentSpec{
Template: core_v1.PodTemplateSpec{
ObjectMeta: meta_v1.ObjectMeta{
Labels: map[string]string{
"app": "real", // Matches the gateway label selector
},
},
},
},
}

v := mockMultiNamespaceGatewaysValidationService(t, *conf, revConfigMap, injectorConfigMap, istioSystemNamespace, istiod_1_19_0, gatewayDeployment)
validations, _, err := v.GetIstioObjectValidations(context.TODO(), conf.KubernetesConfig.ClusterName, "test", "gateways", "first")
require.NoError(err)
require.Len(validations, 1)
key := models.IstioValidationKey{
ObjectType: "gateway",
Name: "first",
Namespace: "test",
}
// Even though the workload is reference properly, because of the PILOT_SCOPE_GATEWAY_TO_NAMESPACE
// the gateway should be marked as invalid.
assert.False(validations[key].Valid)
}

func TestFilterExportToNamespacesVS(t *testing.T) {
assert := assert.New(t)
conf := config.NewConfig()
Expand Down Expand Up @@ -142,7 +224,7 @@ func TestGetVSReferencesNotExisting(t *testing.T) {
assert.Nil(references)
}

func mockMultiNamespaceGatewaysValidationService(t *testing.T) IstioValidationsService {
func mockMultiNamespaceGatewaysValidationService(t *testing.T, cfg config.Config, objects ...runtime.Object) IstioValidationsService {
fakeIstioObjects := []runtime.Object{
&core_v1.ConfigMap{ObjectMeta: meta_v1.ObjectMeta{Name: "istio", Namespace: "istio-system"}},
}
Expand All @@ -165,16 +247,18 @@ func mockMultiNamespaceGatewaysValidationService(t *testing.T) IstioValidationsS
fakeIstioObjects = append(fakeIstioObjects, p.DeepCopyObject())
}

fakeIstioObjects = append(fakeIstioObjects, objects...)

k8s := kubetest.NewFakeK8sClient(fakeIstioObjects...)
cache := SetupBusinessLayer(t, k8s, *config.NewConfig())
cache := SetupBusinessLayer(t, k8s, cfg)
cache.SetRegistryStatus(&kubernetes.RegistryStatus{
Configuration: &kubernetes.RegistryConfiguration{
Gateways: append(getGateway("first", "test"), getGateway("second", "test2")...),
},
})

k8sclients := make(map[string]kubernetes.ClientInterface)
k8sclients[config.Get().KubernetesConfig.ClusterName] = k8s
k8sclients[cfg.KubernetesConfig.ClusterName] = k8s
return IstioValidationsService{userClients: k8sclients, businessLayer: NewWithBackends(k8sclients, k8sclients, nil, nil)}
}

Expand Down
27 changes: 22 additions & 5 deletions business/mesh.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,7 @@ func (in *MeshService) GetMesh(ctx context.Context) (*Mesh, error) {
Revision: istiod.Labels[IstioRevisionLabel],
}

configMapName := in.conf.ExternalServices.Istio.ConfigMapName
if revLabel := istiod.Labels[IstioRevisionLabel]; revLabel != "default" && revLabel != "" {
configMapName = configMapName + "-" + revLabel
}
configMapName := IstioConfigMapName(in.conf, controlPlane.Revision)
Copy link
Collaborator

Choose a reason for hiding this comment

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

the thing I don't see explained anywhere here is why does this pass in the Revision but everywhere else it passes in empty string for revision. How does this work when sometimes you need the revision but other times that is not required to get the configmap?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because basically this is the only place that is handling there being multiple controlplanes. Everywhere else just assumes there's one. When set config.external_services.istio.config_map_name, IstioConfigMapName will always return what you've set there regardless of what revision you pass in. It's a bit of a hack to get around the fact that, right now we expect users to set this manually when using control plane revisions.


controlPlaneConfig, err := getControlPlaneConfiguration(kubeCache, istiod.Namespace, configMapName)
if err != nil {
Expand Down Expand Up @@ -226,6 +223,26 @@ func (in *MeshService) GetMesh(ctx context.Context) (*Mesh, error) {
return mesh, nil
}

// IstioConfigMapName guesses the istio configmap name.
func IstioConfigMapName(conf config.Config, revision string) string {
// If the config map name is explicitly set and it's not the default value, we should always use that.
// Note that this means that the revision is ignored and every controlplane
// will use this configmap regardless of which configmap actually corresponds
// to the revision.
if conf.ExternalServices.Istio.ConfigMapName != "" && conf.ExternalServices.Istio.ConfigMapName != "istio" {
return conf.ExternalServices.Istio.ConfigMapName
}

// If the revision is set, we should use the revisioned configmap name
// otherwise the hardcoded 'istio' value is used.
configMapName := "istio" // As of 1.19 this is hardcoded in the helm charts.
if revision != "default" && revision != "" {
configMapName = configMapName + "-" + revision
}

return configMapName
}

func envVarIsSet(key string, env core_v1.EnvVar) bool {
return env.Name == key && env.Value == "true"
}
Expand Down Expand Up @@ -468,7 +485,7 @@ func (in *MeshService) resolveNetwork(clusterName string) string {

func (in *MeshService) OutboundTrafficPolicy() (*models.OutboundPolicy, error) {
otp := models.OutboundPolicy{Mode: "ALLOW_ANY"}
istioConfig, err := in.kialiCache.GetConfigMap(in.conf.IstioNamespace, in.conf.ExternalServices.Istio.ConfigMapName)
istioConfig, err := in.kialiCache.GetConfigMap(in.conf.IstioNamespace, IstioConfigMapName(in.conf, ""))
if err != nil {
return nil, err
}
Expand Down
86 changes: 85 additions & 1 deletion business/mesh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ trustDomain: cluster.local
}
require := require.New(t)
conf := config.NewConfig()
config.Set(conf)
k8s := kubetest.NewFakeK8sClient(
&core_v1.Namespace{ObjectMeta: v1.ObjectMeta{Name: "istio-system"}},
istiod_1_18_Deployment,
Expand All @@ -683,7 +684,7 @@ trustDomain: cluster.local
istio_1_19_ConfigMap,
)

business.SetupBusinessLayer(t, k8s, *config.NewConfig())
business.SetupBusinessLayer(t, k8s, *conf)

clients := map[string]kubernetes.ClientInterface{conf.KubernetesConfig.ClusterName: k8s}
svc := business.NewWithBackends(clients, clients, nil, nil).Mesh
Expand All @@ -702,6 +703,18 @@ trustDomain: cluster.local
})
require.True(*controlPlane_1_19.Config.EnableAutoMtls)
require.Len(controlPlane_1_19.ManagedClusters, 1)

// Test for setting the configmap name explicitly due to regression: https://github.com/kiali/kiali/issues/6669
conf.ExternalServices.Istio.ConfigMapName = istio_1_19_ConfigMap.Name
config.Set(conf)
svc = business.NewWithBackends(clients, clients, nil, nil).Mesh
mesh, err = svc.GetMesh(context.TODO())
require.NoError(err)

require.Len(mesh.ControlPlanes, 2)
// Both controlplanes should set this to true since both will use the 1.19 configmap.
require.True(*mesh.ControlPlanes[0].Config.EnableAutoMtls)
require.True(*mesh.ControlPlanes[1].Config.EnableAutoMtls)
}

func TestGetMeshRemoteClusters(t *testing.T) {
Expand Down Expand Up @@ -1031,3 +1044,74 @@ func fakeIstiodDeployment(cluster string, manageExternal bool) *apps_v1.Deployme
}
return deployment
}

func TestIstioConfigMapName(t *testing.T) {
testCases := map[string]struct {
conf config.Config
revision string
expected string
}{
"ConfigMapName is empty and revision is default": {
conf: config.Config{
ExternalServices: config.ExternalServices{
Istio: config.IstioConfig{
ConfigMapName: "",
},
},
},
revision: "default",
expected: "istio",
},
"ConfigMapName is empty and revision is v1": {
conf: config.Config{
ExternalServices: config.ExternalServices{
Istio: config.IstioConfig{
ConfigMapName: "",
},
},
},
revision: "v1",
expected: "istio-v1",
},
"ConfigMapName is set and revision is default": {
conf: config.Config{
ExternalServices: config.ExternalServices{
Istio: config.IstioConfig{
ConfigMapName: "my-istio-config",
},
},
},
revision: "default",
expected: "my-istio-config",
},
"ConfigMapName is set and revision is v2": {
conf: config.Config{
ExternalServices: config.ExternalServices{
Istio: config.IstioConfig{
ConfigMapName: "my-istio-config",
},
},
},
revision: "v2",
expected: "my-istio-config",
},
"ConfigMapName is set and revision is empty": {
conf: config.Config{
ExternalServices: config.ExternalServices{
Istio: config.IstioConfig{
ConfigMapName: "my-istio-config",
},
},
},
revision: "",
expected: "my-istio-config",
},
}

for desc, tc := range testCases {
t.Run(desc, func(t *testing.T) {
result := business.IstioConfigMapName(tc.conf, tc.revision)
assert.Equal(t, tc.expected, result)
})
}
}
2 changes: 1 addition & 1 deletion business/namespaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func (in *NamespaceService) GetNamespaces(ctx context.Context) ([]models.Namespa

// determine what the discoverySelectors are by examining the Istio ConfigMap
var discoverySelectors []*meta_v1.LabelSelector
if icm, err := in.kialiCache.GetConfigMap(in.conf.IstioNamespace, in.conf.ExternalServices.Istio.ConfigMapName); err == nil {
if icm, err := in.kialiCache.GetConfigMap(in.conf.IstioNamespace, IstioConfigMapName(in.conf, "")); err == nil {
if ic, err2 := kubernetes.GetIstioConfigMap(icm); err2 == nil {
discoverySelectors = ic.DiscoverySelectors
} else {
Expand Down
2 changes: 1 addition & 1 deletion business/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func (in *TLSService) hasAutoMTLSEnabled(cluster string) bool {
}

cfg := config.Get()
istioConfig, err := kubeCache.GetConfigMap(cfg.IstioNamespace, cfg.ExternalServices.Istio.ConfigMapName)
istioConfig, err := kubeCache.GetConfigMap(cfg.IstioNamespace, IstioConfigMapName(*cfg, ""))
if err != nil {
return true
}
Expand Down