istio
158 строк · 4.9 Кб
1// Copyright Istio Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package controller
16
17import (
18"context"
19"fmt"
20"strings"
21"testing"
22"time"
23
24v1 "k8s.io/api/core/v1"
25metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26"k8s.io/apimachinery/pkg/runtime"
27mcsapi "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1"
28
29meshconfig "istio.io/api/mesh/v1alpha1"
30"istio.io/istio/pilot/pkg/features"
31"istio.io/istio/pilot/pkg/model"
32"istio.io/istio/pkg/config/mesh"
33"istio.io/istio/pkg/kube"
34"istio.io/istio/pkg/kube/kclient/clienttest"
35"istio.io/istio/pkg/kube/mcs"
36"istio.io/istio/pkg/test"
37"istio.io/istio/pkg/test/util/retry"
38)
39
40var serviceExportTimeout = retry.Timeout(time.Second * 2)
41
42func TestServiceExportController(t *testing.T) {
43client := kube.NewFakeClient()
44
45// Configure the environment with cluster-local hosts.
46m := meshconfig.MeshConfig{
47ServiceSettings: []*meshconfig.MeshConfig_ServiceSettings{
48{
49Settings: &meshconfig.MeshConfig_ServiceSettings_Settings{
50ClusterLocal: true,
51},
52Hosts: []string{"*.unexportable-ns.svc.cluster.local", "unexportable-svc.*.svc.cluster.local"},
53},
54},
55}
56env := model.Environment{Watcher: mesh.NewFixedWatcher(&m)}
57env.Init()
58
59sc := newAutoServiceExportController(autoServiceExportOptions{
60Client: client,
61ClusterID: "",
62DomainSuffix: env.DomainSuffix,
63ClusterLocal: env.ClusterLocal(),
64})
65
66stop := test.NewStop(t)
67client.RunAndWait(stop)
68go sc.Run(stop)
69
70t.Run("exportable", func(t *testing.T) {
71createSimpleService(t, client, "exportable-ns", "foo")
72assertServiceExport(t, client, "exportable-ns", "foo", true)
73})
74
75t.Run("unexportable", func(t *testing.T) {
76createSimpleService(t, client, "unexportable-ns", "foo")
77assertServiceExport(t, client, "unexportable-ns", "foo", false)
78})
79
80t.Run("no overwrite", func(t *testing.T) {
81// manually create serviceexport
82export := mcsapi.ServiceExport{
83TypeMeta: metav1.TypeMeta{
84Kind: "ServiceExport",
85APIVersion: features.MCSAPIVersion,
86},
87ObjectMeta: metav1.ObjectMeta{
88Namespace: "exportable-ns",
89Name: "manual-export",
90},
91Status: mcsapi.ServiceExportStatus{
92Conditions: []mcsapi.ServiceExportCondition{
93{
94Type: mcsapi.ServiceExportValid,
95},
96},
97},
98}
99
100_, err := client.Dynamic().Resource(mcs.ServiceExportGVR).Namespace("exportable-ns").Create(
101context.TODO(), toUnstructured(&export), metav1.CreateOptions{})
102if err != nil {
103t.Fatalf("Unexpected error %v", err)
104}
105
106// create the associated service
107// no need for assertions, just trying to ensure no errors
108createSimpleService(t, client, "exportable-ns", "manual-export")
109
110// assert that we didn't wipe out the pre-existing serviceexport status
111assertServiceExportHasCondition(t, client, "exportable-ns", "manual-export",
112mcsapi.ServiceExportValid)
113})
114}
115
116func createSimpleService(t *testing.T, client kube.Client, ns string, name string) {
117t.Helper()
118clienttest.NewWriter[*v1.Service](t, client).Create(&v1.Service{
119ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: ns},
120})
121}
122
123func assertServiceExport(t *testing.T, client kube.Client, ns, name string, shouldBePresent bool) {
124t.Helper()
125retry.UntilSuccessOrFail(t, func() error {
126got, err := client.Dynamic().Resource(mcs.ServiceExportGVR).Namespace(ns).Get(context.TODO(), name, metav1.GetOptions{})
127
128if err != nil && !strings.Contains(err.Error(), "not found") {
129return fmt.Errorf("unexpected error %v", err)
130}
131isPresent := got != nil
132if isPresent != shouldBePresent {
133return fmt.Errorf("unexpected serviceexport state. IsPresent: %v, ShouldBePresent: %v, name: %v, namespace: %v", isPresent, shouldBePresent, name, ns)
134}
135return nil
136}, serviceExportTimeout)
137}
138
139func assertServiceExportHasCondition(t *testing.T, client kube.Client, ns, name string, condition mcsapi.ServiceExportConditionType) {
140t.Helper()
141retry.UntilSuccessOrFail(t, func() error {
142gotU, err := client.Dynamic().Resource(mcs.ServiceExportGVR).Namespace(ns).Get(context.TODO(), name, metav1.GetOptions{})
143if err != nil {
144return fmt.Errorf("unexpected error %v", err)
145}
146
147got := &mcsapi.ServiceExport{}
148if err := runtime.DefaultUnstructuredConverter.FromUnstructured(gotU.Object, got); err != nil {
149return err
150}
151
152if got.Status.Conditions == nil || len(got.Status.Conditions) == 0 || got.Status.Conditions[0].Type != condition {
153return fmt.Errorf("condition incorrect or not found")
154}
155
156return nil
157}, serviceExportTimeout)
158}
159