blob: 41a708e32646a00f45bda3bf516561b429220ae5 [file] [log] [blame]
//go:build integ
// +build integ
// 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 pilot
import (
"context"
"errors"
"fmt"
"strings"
"testing"
"time"
)
import (
"istio.io/client-go/pkg/apis/networking/v1alpha3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
import (
"github.com/apache/dubbo-go-pixiu/pilot/pkg/controller/workloadentry"
"github.com/apache/dubbo-go-pixiu/pilot/pkg/features"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/check"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/common/ports"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/deployment"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/kube"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/match"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/label"
"github.com/apache/dubbo-go-pixiu/pkg/test/scopes"
"github.com/apache/dubbo-go-pixiu/pkg/test/util/retry"
"github.com/apache/dubbo-go-pixiu/tests/integration/pilot/common"
)
func GetAdditionVMImages() []string {
var out []echo.VMDistro
for distro, image := range kube.VMImages {
if distro == echo.DefaultVMDistro {
continue
}
out = append(out, image)
}
return out
}
func TestVmOSPost(t *testing.T) {
framework.
NewTest(t).
Features("traffic.reachability").
Label(label.Postsubmit).
Run(func(t framework.TestContext) {
if t.Settings().Skip(echo.VM) {
t.Skip("VM tests are disabled")
}
b := deployment.New(t, t.Clusters().Primaries().Default())
images := GetAdditionVMImages()
for _, image := range images {
b = b.WithConfig(echo.Config{
Service: "vm-" + strings.ReplaceAll(image, "_", "-"),
Namespace: apps.Namespace,
Ports: ports.All(),
DeployAsVM: true,
VMDistro: image,
Subsets: []echo.SubsetConfig{{}},
})
}
instances := b.BuildOrFail(t)
for i, image := range images {
i, image := i, image
t.NewSubTest(image).RunParallel(func(t framework.TestContext) {
for _, tt := range common.VMTestCases(t, echo.Instances{instances[i]}, &apps) {
tt.Run(t, apps.Namespace.Name())
}
})
}
})
}
func TestVMRegistrationLifecycle(t *testing.T) {
t.Skip("https://github.com/istio/istio/issues/33154")
framework.
NewTest(t).
RequiresSingleCluster().
Features("vm.autoregistration").
Run(func(t framework.TestContext) {
if t.Settings().Skip(echo.VM) {
t.Skip()
}
scaleDeploymentOrFail(t, "istiod", i.Settings().SystemNamespace, 2)
client := match.Cluster(t.Clusters().Default()).FirstOrFail(t, apps.A)
// TODO test multi-network (must be shared control plane but on different networks)
var autoVM echo.Instance
_ = deployment.New(t).
With(&autoVM, echo.Config{
Namespace: apps.Namespace,
Service: "auto-vm",
Ports: ports.All(),
DeployAsVM: true,
AutoRegisterVM: true,
}).BuildOrFail(t)
t.NewSubTest("initial registration").Run(func(t framework.TestContext) {
retry.UntilSuccessOrFail(t, func() error {
result, err := client.Call(echo.CallOptions{
To: autoVM,
Port: autoVM.Config().Ports[0],
Retry: echo.Retry{
NoRetry: true,
},
})
return check.And(
check.NoError(),
check.OK()).Check(result, err)
}, retry.Timeout(15*time.Second))
})
t.NewSubTest("reconnect reuses WorkloadEntry").Run(func(t framework.TestContext) {
// ensure we have two pilot instances, other tests can pass before the second one comes up
retry.UntilSuccessOrFail(t, func() error {
pilotRes, err := t.Clusters().Default().CoreV1().Pods(i.Settings().SystemNamespace).
List(context.TODO(), metav1.ListOptions{LabelSelector: "istio=pilot"})
if err != nil {
return err
}
if len(pilotRes.Items) != 2 {
return errors.New("expected 2 pilots")
}
return nil
}, retry.Timeout(10*time.Second))
// get the initial workload entry state
entries := getWorkloadEntriesOrFail(t, autoVM)
if len(entries) != 1 {
t.Fatalf("expected exactly 1 WorkloadEntry but got %d", len(entries))
}
initialWLE := entries[0]
// keep force-disconnecting until we observe a reconnect to a different istiod instance
initialPilot := initialWLE.Annotations[workloadentry.WorkloadControllerAnnotation]
disconnectProxy(t, initialPilot, autoVM)
retry.UntilSuccessOrFail(t, func() error {
entries := getWorkloadEntriesOrFail(t, autoVM)
if len(entries) != 1 || entries[0].UID != initialWLE.UID {
t.Fatalf("WorkloadEntry was cleaned up unexpectedly")
}
currentPilot := entries[0].Annotations[workloadentry.WorkloadControllerAnnotation]
if currentPilot == initialPilot || !strings.HasPrefix(currentPilot, "istiod-") {
disconnectProxy(t, currentPilot, autoVM)
return errors.New("expected WorkloadEntry to be updated by other pilot")
}
return nil
}, retry.Delay(5*time.Second))
})
t.NewSubTest("disconnect deletes WorkloadEntry").Run(func(t framework.TestContext) {
d := fmt.Sprintf("%s-%s", autoVM.Config().Service, "v1")
scaleDeploymentOrFail(t, d, autoVM.Config().Namespace.Name(), 0)
// it should take at most just over GracePeriod to cleanup if all pilots are healthy
retry.UntilSuccessOrFail(t, func() error {
if len(getWorkloadEntriesOrFail(t, autoVM)) > 0 {
return errors.New("expected 0 WorkloadEntries")
}
return nil
}, retry.Timeout(2*features.WorkloadEntryCleanupGracePeriod+(2*time.Second)))
})
})
}
func disconnectProxy(t framework.TestContext, pilot string, instance echo.Instance) {
proxyID := strings.Join([]string{instance.WorkloadsOrFail(t)[0].PodName(), instance.Config().Namespace.Name()}, ".")
cmd := "pilot-discovery request GET /debug/force_disconnect?proxyID=" + proxyID
stdOut, _, err := t.Clusters().Default().
PodExec(pilot, i.Settings().SystemNamespace, "discovery", cmd)
if err != nil {
scopes.Framework.Warnf("failed to force disconnect %s: %v: %v", proxyID, stdOut, err)
}
}
func scaleDeploymentOrFail(t framework.TestContext, name, namespace string, scale int32) {
s, err := t.Clusters().Default().AppsV1().Deployments(namespace).
GetScale(context.TODO(), name, metav1.GetOptions{})
if err != nil {
t.Fatal(err)
}
s.Spec.Replicas = scale
_, err = t.Clusters().Default().AppsV1().Deployments(namespace).
UpdateScale(context.TODO(), name, s, metav1.UpdateOptions{})
if err != nil {
t.Fatal(err)
}
}
func getWorkloadEntriesOrFail(t framework.TestContext, vm echo.Instance) []*v1alpha3.WorkloadEntry {
res, err := t.Clusters().Default().Istio().NetworkingV1alpha3().
WorkloadEntries(vm.Config().Namespace.Name()).
List(context.TODO(), metav1.ListOptions{LabelSelector: "app=" + vm.Config().Service})
if err != nil {
t.Fatal(err)
}
return res.Items
}