diff --git a/.github/workflows/kind.yml b/.github/workflows/kind.yml index a27c3d2c9ab..06ce4ee4fe5 100644 --- a/.github/workflows/kind.yml +++ b/.github/workflows/kind.yml @@ -740,7 +740,7 @@ jobs: retention-days: 30 conduct-connectivity-test: - name: Test connectivity using antctl test command + name: Test connectivity using 'antctl check' command needs: [ build-antrea-coverage-image ] runs-on: [ ubuntu-latest ] steps: @@ -770,14 +770,14 @@ jobs: - name: Create Kind Cluster run: | kind create cluster --config ci/kind/config-3nodes.yml - - name: load docker image into kind + - name: Load docker image into kind run: | kind load docker-image antrea/antrea-controller-ubuntu-coverage:latest antrea/antrea-agent-ubuntu-coverage:latest kubectl apply -f build/yamls/antrea.yml - - name: build antctl binaries + - name: Build antctl binary run: | make antctl-linux - - name: run antctl command + - name: Run antctl command run: | ./bin/antctl-linux check installation diff --git a/pkg/antctl/antctl.go b/pkg/antctl/antctl.go index ab323e165e0..96b94e36d2d 100644 --- a/pkg/antctl/antctl.go +++ b/pkg/antctl/antctl.go @@ -24,12 +24,12 @@ import ( "antrea.io/antrea/pkg/agent/apiserver/handlers/podinterface" "antrea.io/antrea/pkg/agent/apiserver/handlers/serviceexternalip" fallbackversion "antrea.io/antrea/pkg/antctl/fallback/version" + checkinstallation "antrea.io/antrea/pkg/antctl/raw/check/installation" "antrea.io/antrea/pkg/antctl/raw/featuregates" "antrea.io/antrea/pkg/antctl/raw/multicluster" "antrea.io/antrea/pkg/antctl/raw/proxy" "antrea.io/antrea/pkg/antctl/raw/set" "antrea.io/antrea/pkg/antctl/raw/supportbundle" - "antrea.io/antrea/pkg/antctl/raw/test" "antrea.io/antrea/pkg/antctl/raw/traceflow" "antrea.io/antrea/pkg/antctl/raw/upgrade/apistorage" "antrea.io/antrea/pkg/antctl/transform/addressgroup" @@ -641,7 +641,7 @@ $ antctl get podmulticaststats pod -n namespace`, }, rawCommands: []rawCommand{ { - cobraCommand: test.Command(), + cobraCommand: checkinstallation.Command(), supportAgent: false, supportController: false, commandGroup: check, diff --git a/pkg/antctl/raw/test/client.go b/pkg/antctl/raw/check/client.go similarity index 58% rename from pkg/antctl/raw/test/client.go rename to pkg/antctl/raw/check/client.go index ac8670c1f26..f0ee40e1dcd 100644 --- a/pkg/antctl/raw/test/client.go +++ b/pkg/antctl/raw/check/client.go @@ -12,23 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -package test +package check import ( "bytes" "context" "fmt" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/tools/remotecommand" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/remotecommand" ) type Client struct { @@ -37,11 +35,6 @@ type Client struct { clusterName string } -type ExecResult struct { - Stdout bytes.Buffer - Stderr bytes.Buffer -} - func NewClient() (*Client, error) { rules := clientcmd.NewDefaultClientConfigLoadingRules() @@ -76,24 +69,16 @@ func NewClient() (*Client, error) { }, nil } -func (c *Client) ClusterName() (name string) { - return c.clusterName +func (c *Client) GetClientSet() kubernetes.Interface { + return c.clientSet } -func (c *Client) CreateService(ctx context.Context, namespace string, service *corev1.Service, opts metav1.CreateOptions) (*corev1.Service, error) { - return c.clientSet.CoreV1().Services(namespace).Create(ctx, service, opts) -} - -func (c *Client) CreateDeployment(ctx context.Context, namespace string, deployment *appsv1.Deployment, opts metav1.CreateOptions) (*appsv1.Deployment, error) { - return c.clientSet.AppsV1().Deployments(namespace).Create(ctx, deployment, opts) -} - -func (c *Client) GetDeployment(ctx context.Context, namespace, name string, opts metav1.GetOptions) (*appsv1.Deployment, error) { - return c.clientSet.AppsV1().Deployments(namespace).Get(ctx, name, opts) +func (c *Client) ClusterName() (name string) { + return c.clusterName } func (c *Client) DeploymentIsReady(ctx context.Context, namespace, deploymentName string) (bool, error) { - deployment, err := c.GetDeployment(ctx, namespace, deploymentName, metav1.GetOptions{}) + deployment, err := c.clientSet.AppsV1().Deployments(namespace).Get(ctx, deploymentName, metav1.GetOptions{}) if err != nil { return false, err } @@ -118,32 +103,8 @@ func (c *Client) DeploymentIsReady(ctx context.Context, namespace, deploymentNam return false, nil } -func (c *Client) CreateNamespace(ctx context.Context, namespace string, opts metav1.CreateOptions) (*corev1.Namespace, error) { - return c.clientSet.CoreV1().Namespaces().Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}, opts) -} - -func (c *Client) GetNamespace(ctx context.Context, namespace string, options metav1.GetOptions) (*corev1.Namespace, error) { - return c.clientSet.CoreV1().Namespaces().Get(ctx, namespace, options) -} - -func (c *Client) DeleteNamespace(ctx context.Context, namespace string, opts metav1.DeleteOptions) error { - return c.clientSet.CoreV1().Namespaces().Delete(ctx, namespace, opts) -} - -func (c *Client) ListPods(ctx context.Context, namespace string, options metav1.ListOptions) (*corev1.PodList, error) { - return c.clientSet.CoreV1().Pods(namespace).List(ctx, options) -} - func (c *Client) ExecInPod(ctx context.Context, namespace, pod, container string, command []string) (bytes.Buffer, error) { req := c.clientSet.CoreV1().RESTClient().Post().Resource("pods").Name(pod).Namespace(namespace).SubResource("exec") - - scheme := runtime.NewScheme() - if err := corev1.AddToScheme(scheme); err != nil { - return bytes.Buffer{}, fmt.Errorf("error adding to scheme: %w", err) - } - - parameterCodec := runtime.NewParameterCodec(scheme) - req.VersionedParams(&corev1.PodExecOptions{ Command: command, Container: container, @@ -151,32 +112,21 @@ func (c *Client) ExecInPod(ctx context.Context, namespace, pod, container string Stdout: true, Stderr: true, TTY: false, - }, parameterCodec) - + }, scheme.ParameterCodec) exec, err := remotecommand.NewSPDYExecutor(c.config, "POST", req.URL()) if err != nil { return bytes.Buffer{}, fmt.Errorf("error while creating executor: %w", err) } - - result := &ExecResult{} - + var stdout, stderr bytes.Buffer err = exec.StreamWithContext(ctx, remotecommand.StreamOptions{ Stdin: nil, - Stdout: &result.Stdout, - Stderr: &result.Stderr, + Stdout: &stdout, + Stderr: &stderr, Tty: false, }) if err != nil { return bytes.Buffer{}, fmt.Errorf("error in stream: %w", err) } - - if errString := result.Stderr.String(); errString != "" { - return bytes.Buffer{}, fmt.Errorf("command failed: %s", errString) - } - - return result.Stdout, nil -} - -func (c *Client) GetDaemonSet(ctx context.Context, namespace, name string, opts metav1.GetOptions) (*appsv1.DaemonSet, error) { - return c.clientSet.AppsV1().DaemonSets(namespace).Get(ctx, name, opts) + fmt.Fprint(&stdout, stderr.String()) + return stdout, nil } diff --git a/pkg/antctl/raw/test/command.go b/pkg/antctl/raw/check/installation/command.go similarity index 50% rename from pkg/antctl/raw/test/command.go rename to pkg/antctl/raw/check/installation/command.go index 8499844a91a..0a85262e1df 100644 --- a/pkg/antctl/raw/test/command.go +++ b/pkg/antctl/raw/check/installation/command.go @@ -12,14 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -package test +package installation import ( "bytes" "context" "crypto/rand" "fmt" - "io" "os" "time" @@ -28,27 +27,33 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + + client "antrea.io/antrea/pkg/antctl/raw/check" ) func Command() *cobra.Command { - client, err := NewClient() + client, err := client.NewClient() if err != nil { - fmt.Fprintf(os.Stderr, "Unable to create kubernetes client: %s", err) + fmt.Fprintf(os.Stderr, "Unable to create Kubernetes client: %s", err) } - k8sClient := client - check := NewAntreaConnectivityCheck(k8sClient) + check := NewConnectivityCheck(client) command := &cobra.Command{ Use: "installation", Short: "Runs post installation checks", RunE: func(cmd *cobra.Command, args []string) error { - fmt.Fprintf(check.Writer, "Test starting \n") return check.Run(context.Background()) }, } - command.Flags().StringVarP(&check.antreaNamespace, "namespace", "n", check.antreaNamespace, "Configure namespace in which antrea is running") + command.Flags().StringVarP(&check.antreaNamespace, "Namespace", "n", check.antreaNamespace, "Configure Namespace in which Antrea is running") return command } +func init() { + RegisterTest("Pod-to-Pod Connectivity", &PodtoPodConnectivityTest{}) + RegisterTest("Pod-to-Internet Connectivity", &PodtoInternetConnectivityTest{}) +} + const ( connectivityCheckNamespace = "antrea-test" clientDeploymentName = "test-client" @@ -57,25 +62,40 @@ const ( kindEchoName = "echo" kindClientName = "client" agentDaemonSetName = "antrea-agent" + deploymentImage = "registry.k8s.io/e2e-test-images/agnhost:2.29" ) -func (k *k8sConnectivityParams) Run(ctx context.Context) error { - c, err := k.initClients(ctx) +type Test interface { + Run(ctx context.Context, testContext *TestContext) error +} + +var testsRegistry = make(map[string]Test) + +func RegisterTest(name string, test Test) { + testsRegistry[name] = test +} + +type TestContext struct { + client connectivityCheck +} + +func (k *connectivityCheck) Run(ctx context.Context) error { + k.Log("Test starting") + err := k.initClients(ctx) if err != nil { return err } - k.clients = c - err = k.deploy(ctx) if err != nil { return err } - if err := k.validateDeployment(ctx); err != nil { return err } - k.validatePodToPod(ctx) - k.validatePodInternetConnectivity(ctx) + testContext := &TestContext{ + client: *k, + } + runAllTests(ctx, testContext) k.Log("Test finished") k.Log("Deleting deployments") if err := k.deleteDeployments(ctx, k.client); err != nil { @@ -86,11 +106,23 @@ func (k *k8sConnectivityParams) Run(ctx context.Context) error { return nil } +func runAllTests(ctx context.Context, testContext *TestContext) { + for name, test := range testsRegistry { + testContext.client.Log("-------------------------------------------------------------------------------------------") + testContext.client.Log("Running test: %s\n", name) + if err := test.Run(ctx, testContext); err != nil { + testContext.client.Log("Test %s failed: %s", name, err) + } else { + testContext.client.Log("Test %s passed", name) + } + } +} + func agnhostConnectCommand(target string) []string { return []string{"/agnhost", "connect", target, "--timeout=5s"} } -func newService(name string, selector map[string]string, portName string, port int) *corev1.Service { +func newService(name string, selector map[string]string, port int) *corev1.Service { return &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -168,21 +200,18 @@ func newDeployment(p deploymentParameters) *appsv1.Deployment { } } -type k8sConnectivityParams struct { - clients *deploymentClients +type connectivityCheck struct { client k8sClientOperations - Writer io.Writer antreaNamespace string clientPods *corev1.PodList echoPods map[string]string namespace string } -func NewAntreaConnectivityCheck(client k8sClientOperations) *k8sConnectivityParams { - return &k8sConnectivityParams{ +func NewConnectivityCheck(client k8sClientOperations) *connectivityCheck { + return &connectivityCheck{ client: client, antreaNamespace: "kube-system", - Writer: os.Stdout, namespace: generateRandomNamespace(connectivityCheckNamespace), } } @@ -200,137 +229,55 @@ func generateRandomNamespace(baseName string) string { return fmt.Sprintf("%s-%s", baseName, string(bytes)) } -func (k *k8sConnectivityParams) validatePodToPod(ctx context.Context) { - //conducts Pod to Pod connectivity tests within same node and different node - for _, clientPod := range k.clientPods.Items { - for echoName, echoIP := range k.echoPods { - var ( - srcPod = k.namespace + "/" + clientPod.Name - dstPod = k.namespace + "/" + echoName - success = true - ) - k.Header("Validating from pod %s to pod %s...", srcPod, dstPod) - _, err := k.client.ExecInPod(ctx, k.namespace, clientPod.Name, "", agnhostConnectCommand(echoIP+":80")) - if err != nil { - k.Log("curl connectivity check command failed: %s", err) - success = false - } - - if success { - k.Log("client pod %s was able to communicate with echo pod %s (%s)", clientPod.Name, echoName, echoIP) - } else { - k.Log("client pod %s was not able to communicate with echo pod %s (%s)", clientPod.Name, echoName, echoIP) - } - - k.Relax() - } - } -} - -func (k *k8sConnectivityParams) validatePodInternetConnectivity(ctx context.Context) error { - for _, clientPod := range k.clientPods.Items { - var ( - srcPod = k.namespace + "/" + clientPod.Name - success = true - ) - - k.Header("Validating connectivity from pod %s to the world (google.com)...", srcPod) - _, err := k.client.ExecInPod(ctx, k.namespace, clientPod.Name, clientDeploymentName, agnhostConnectCommand("google.com:80")) - if err != nil { - k.Log("Connectivity test from pod %s to google.com failed: %s", srcPod, err) - success = false - } - - if success { - k.Log("Pod %s was able to connect to google.com", srcPod) - } - - k.Relax() - } - return nil -} - -func (k *k8sConnectivityParams) Relax() { - time.Sleep(2 * time.Second) -} - const podReadyTimeout = 5 * time.Minute -func (k *k8sConnectivityParams) deleteDeployments(ctx context.Context, client k8sClientOperations) error { - k.Log("[%s] Deleting connectivity check deployments...", client.ClusterName()) - client.DeleteNamespace(ctx, k.namespace, metav1.DeleteOptions{}) - - _, err := client.GetNamespace(ctx, k.namespace, metav1.GetOptions{}) - if err == nil { - k.Log("[%s] Waiting for namespace %s to disappear", client.ClusterName(), k.namespace) - for err == nil { - time.Sleep(time.Second) - _, err = client.GetNamespace(ctx, k.namespace, metav1.GetOptions{}) +func (k *connectivityCheck) deleteDeployments(ctx context.Context, client k8sClientOperations) error { + k.Log("[%s] Deleting connectivity check Deployments...", client.ClusterName()) + client.GetClientSet().CoreV1().Namespaces().Delete(ctx, k.namespace, metav1.DeleteOptions{}) + k.Log("[%s] Waiting for Namespace %s to disappear", client.ClusterName(), k.namespace) + err := wait.PollUntilContextTimeout(ctx, 1*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { + _, err := client.GetClientSet().CoreV1().Namespaces().Get(ctx, k.namespace, metav1.GetOptions{}) + if err != nil { + return true, nil } - } - - return nil -} - -func (k *k8sConnectivityParams) deploymentList() (srcList []string, dstList []string) { - srcList = []string{clientDeploymentName, echoSameNodeDeploymentName} - dstList = append(dstList, echoOtherNodeDeploymentName) - - return srcList, dstList + return false, nil + }) + return err } -type deploymentClients struct { - source k8sClientOperations - destination k8sClientOperations -} - -func (d *deploymentClients) clients() []k8sClientOperations { - return []k8sClientOperations{d.source} -} - -func (k *k8sConnectivityParams) initClients(ctx context.Context) (*deploymentClients, error) { - c := &deploymentClients{ - source: k.client, - destination: k.client, - } - - _, err := k.client.GetDaemonSet(ctx, k.antreaNamespace, agentDaemonSetName, metav1.GetOptions{}) +func (k *connectivityCheck) initClients(ctx context.Context) error { + _, err := k.client.GetClientSet().AppsV1().DaemonSets(k.antreaNamespace).Get(ctx, agentDaemonSetName, metav1.GetOptions{}) if err != nil { - k.Log("Unable to determine status of Antrea DaemonSet.") - return nil, fmt.Errorf("Unable to determine status of antrea DaemonSet: %w", err) + return fmt.Errorf("Unable to determine status of Antrea DaemonSet: %w", err) } - - return c, nil + return nil } -func (k *k8sConnectivityParams) deploy(ctx context.Context) error { +func (k *connectivityCheck) deploy(ctx context.Context) error { var srcDeploymentNeeded, dstDeploymentNeeded bool - - _, err := k.clients.source.GetNamespace(ctx, k.namespace, metav1.GetOptions{}) + _, err := k.client.GetClientSet().CoreV1().Namespaces().Get(ctx, k.namespace, metav1.GetOptions{}) if err != nil { srcDeploymentNeeded = true dstDeploymentNeeded = true - - k.Log("[%s] Creating namespace for connectivity check...", k.clients.source.ClusterName()) - _, err = k.clients.source.CreateNamespace(ctx, k.namespace, metav1.CreateOptions{}) + k.Log("[%s] Creating Namespace for connectivity check...", k.client.ClusterName()) + _, err = k.client.GetClientSet().CoreV1().Namespaces().Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: k.namespace}}, metav1.CreateOptions{}) if err != nil { - return fmt.Errorf("unable to create namespace %s: %s", k.namespace, err) + return fmt.Errorf("Unable to create Namespace %s: %s", k.namespace, err) } } if srcDeploymentNeeded { - k.Log("[%s] Deploying echo-same-node service...", k.clients.source.ClusterName()) - svc := newService(echoSameNodeDeploymentName, map[string]string{"name": echoSameNodeDeploymentName}, "http", 80) - _, err = k.clients.source.CreateService(ctx, k.namespace, svc, metav1.CreateOptions{}) + k.Log("[%s] Deploying echo-same-node service...", k.client.ClusterName()) + svc := newService(echoSameNodeDeploymentName, map[string]string{"name": echoSameNodeDeploymentName}, 80) + _, err = k.client.GetClientSet().CoreV1().Services(k.namespace).Create(ctx, svc, metav1.CreateOptions{}) if err != nil { return err } - echoDeployment := newDeployment(deploymentParameters{ Name: echoSameNodeDeploymentName, Kind: kindEchoName, Port: 80, - Image: "registry.k8s.io/e2e-test-images/agnhost:2.29", + Image: deploymentImage, Command: []string{"/agnhost", "netexec", "--http-port=80"}, Affinity: &corev1.Affinity{ PodAffinity: &corev1.PodAffinity{ @@ -359,40 +306,37 @@ func (k *k8sConnectivityParams) deploy(ctx context.Context) error { }, Labels: map[string]string{"app": echoSameNodeDeploymentName}, }) - - _, err = k.clients.source.CreateDeployment(ctx, k.namespace, echoDeployment, metav1.CreateOptions{}) + _, err = k.client.GetClientSet().AppsV1().Deployments(k.namespace).Create(ctx, echoDeployment, metav1.CreateOptions{}) if err != nil { - return fmt.Errorf("unable to create deployment %s: %s", echoSameNodeDeploymentName, err) + return fmt.Errorf("unable to create Deployment %s: %s", echoSameNodeDeploymentName, err) } - - k.Log("[%s] Deploying client deployment...", k.clients.source.ClusterName()) + k.Log("[%s] Deploying client Deployment...", k.client.ClusterName()) clientDeployment := newDeployment(deploymentParameters{ Name: clientDeploymentName, Kind: kindClientName, - Image: "registry.k8s.io/e2e-test-images/agnhost:2.29", + Image: deploymentImage, Command: []string{"/agnhost", "pause"}, Port: 80, Labels: map[string]string{"app": clientDeploymentName}, }) - _, err = k.clients.source.CreateDeployment(ctx, k.namespace, clientDeployment, metav1.CreateOptions{}) + _, err = k.client.GetClientSet().AppsV1().Deployments(k.namespace).Create(ctx, clientDeployment, metav1.CreateOptions{}) if err != nil { - return fmt.Errorf("unable to create deployment %s: %s", clientDeploymentName, err) + return fmt.Errorf("unable to create Deployment %s: %s", clientDeploymentName, err) } } if dstDeploymentNeeded { - k.Log("[%s] Deploying echo-other-node service...", k.clients.destination.ClusterName()) - svc := newService(echoOtherNodeDeploymentName, map[string]string{"name": echoOtherNodeDeploymentName}, "http", 80) - _, err = k.clients.destination.CreateService(ctx, k.namespace, svc, metav1.CreateOptions{}) + k.Log("[%s] Deploying echo-other-node Service...", k.client.ClusterName()) + svc := newService(echoOtherNodeDeploymentName, map[string]string{"name": echoOtherNodeDeploymentName}, 80) + _, err = k.client.GetClientSet().CoreV1().Services(k.namespace).Create(ctx, svc, metav1.CreateOptions{}) if err != nil { return err } - echoOtherNodeDeployment := newDeployment(deploymentParameters{ Name: echoOtherNodeDeploymentName, Kind: kindEchoName, Port: 80, - Image: "k8s.gcr.io/e2e-test-images/agnhost:2.31", + Image: deploymentImage, Command: []string{"/agnhost", "netexec", "--http-port=80"}, Affinity: &corev1.Affinity{ PodAntiAffinity: &corev1.PodAntiAffinity{ @@ -410,19 +354,17 @@ func (k *k8sConnectivityParams) deploy(ctx context.Context) error { }, Labels: map[string]string{"app": echoOtherNodeDeploymentName}, }) - - _, err = k.clients.destination.CreateDeployment(ctx, k.namespace, echoOtherNodeDeployment, metav1.CreateOptions{}) + _, err = k.client.GetClientSet().AppsV1().Deployments(k.namespace).Create(ctx, echoOtherNodeDeployment, metav1.CreateOptions{}) if err != nil { - return fmt.Errorf("unable to create deployment %s: %s", echoOtherNodeDeploymentName, err) + return fmt.Errorf("unable to create Deployment %s: %s", echoOtherNodeDeploymentName, err) } } - return nil } -func (k *k8sConnectivityParams) waitForDeploymentsReady(ctx context.Context, client k8sClientOperations, deployments []string, interval, timeout time.Duration) error { +func (k *connectivityCheck) waitForDeploymentsReady(ctx context.Context, client k8sClientOperations, deployments []string, interval, timeout time.Duration) error { for _, deployment := range deployments { - k.Log("[%s] Waiting for deployment %s to become ready...", client.ClusterName(), deployment) + k.Log("[%s] Waiting for Deployment %s to become ready...", client.ClusterName(), deployment) err := wait.PollUntilContextTimeout(ctx, interval, timeout, false, func(ctx context.Context) (bool, error) { ready, err := client.DeploymentIsReady(ctx, k.namespace, deployment) if err != nil { @@ -431,63 +373,52 @@ func (k *k8sConnectivityParams) waitForDeploymentsReady(ctx context.Context, cli return ready, nil }) if err != nil { - return fmt.Errorf("waiting for deployment %s to become ready has been interrupted: %w", deployment, err) + return fmt.Errorf("waiting for Deployment %s to become ready has been interrupted: %w", deployment, err) } k.Log("[%s] Deployment %s is ready.", client.ClusterName(), deployment) } return nil } -func (k *k8sConnectivityParams) validateDeployment(ctx context.Context) error { +func (k *connectivityCheck) validateDeployment(ctx context.Context) error { var err error - - srcDeployments, dstDeployments := k.deploymentList() - if err := k.waitForDeploymentsReady(ctx, k.clients.source, srcDeployments, time.Second, podReadyTimeout); err != nil { + srcDeployments := []string{clientDeploymentName, echoSameNodeDeploymentName} + dstDeployments := []string{echoOtherNodeDeploymentName} + if err := k.waitForDeploymentsReady(ctx, k.client, srcDeployments, time.Second, podReadyTimeout); err != nil { return err } - if err := k.waitForDeploymentsReady(ctx, k.clients.destination, dstDeployments, time.Second, podReadyTimeout); err != nil { + if err := k.waitForDeploymentsReady(ctx, k.client, dstDeployments, time.Second, podReadyTimeout); err != nil { return err } - - k.clientPods, err = k.client.ListPods(ctx, k.namespace, metav1.ListOptions{LabelSelector: "kind=" + kindClientName}) + k.clientPods, err = k.client.GetClientSet().CoreV1().Pods(k.namespace).List(ctx, metav1.ListOptions{LabelSelector: "kind=" + kindClientName}) if err != nil { - return fmt.Errorf("unable to list client pods: %s", err) + return fmt.Errorf("Unable to list Client Pods: %s", err) } - k.echoPods = map[string]string{} - for _, client := range k.clients.clients() { - echoPods, err := client.ListPods(ctx, k.namespace, metav1.ListOptions{LabelSelector: "kind=" + kindEchoName}) - if err != nil { - return fmt.Errorf("unable to list echo pods: %s", err) - } - for _, echoPod := range echoPods.Items { - k.echoPods[echoPod.Name] = echoPod.Status.PodIP - } + echoPods, err := k.client.GetClientSet().CoreV1().Pods(k.namespace).List(ctx, metav1.ListOptions{LabelSelector: "kind=" + kindEchoName}) + if err != nil { + return fmt.Errorf("Unable to list Echo Pods: %s", err) + } + for _, echoPod := range echoPods.Items { + k.echoPods[echoPod.Name] = echoPod.Status.PodIP } - fmt.Fprintf(k.Writer, "Deployment is validated\n") + k.Log("Deployment is validated successfully") return nil } -func (k *k8sConnectivityParams) Log(format string, a ...interface{}) { - fmt.Fprintf(k.Writer, format+"\n", a...) +func (k *connectivityCheck) Log(format string, a ...interface{}) { + fmt.Fprintf(os.Stdout, format+"\n", a...) } -func (k *k8sConnectivityParams) Header(format string, a ...interface{}) { +func (k *connectivityCheck) Header(format string, a ...interface{}) { k.Log("-------------------------------------------------------------------------------------------") k.Log(format, a...) k.Log("-------------------------------------------------------------------------------------------") } type k8sClientOperations interface { - CreateService(ctx context.Context, namespace string, service *corev1.Service, opts metav1.CreateOptions) (*corev1.Service, error) - CreateDeployment(ctx context.Context, namespace string, deployment *appsv1.Deployment, opts metav1.CreateOptions) (*appsv1.Deployment, error) - GetDeployment(ctx context.Context, namespace, name string, opts metav1.GetOptions) (*appsv1.Deployment, error) - GetDaemonSet(ctx context.Context, namespace, name string, options metav1.GetOptions) (*appsv1.DaemonSet, error) DeploymentIsReady(ctx context.Context, namespace, deploymentName string) (bool, error) - DeleteNamespace(ctx context.Context, namespace string, opts metav1.DeleteOptions) error - CreateNamespace(ctx context.Context, namespace string, opts metav1.CreateOptions) (*corev1.Namespace, error) - GetNamespace(ctx context.Context, namespace string, options metav1.GetOptions) (*corev1.Namespace, error) - ListPods(ctx context.Context, namespace string, options metav1.ListOptions) (*corev1.PodList, error) ExecInPod(ctx context.Context, namespace, pod, container string, command []string) (bytes.Buffer, error) ClusterName() (name string) + GetClientSet() kubernetes.Interface } diff --git a/pkg/antctl/raw/check/installation/test_PodToInternet.go b/pkg/antctl/raw/check/installation/test_PodToInternet.go new file mode 100644 index 00000000000..f46c1ee7af8 --- /dev/null +++ b/pkg/antctl/raw/check/installation/test_PodToInternet.go @@ -0,0 +1,37 @@ +// Copyright 2024 Antrea 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 installation + +import ( + "context" + "fmt" +) + +type PodtoInternetConnectivityTest struct{} + +func (t *PodtoInternetConnectivityTest) Run(ctx context.Context, testContext *TestContext) error { + for _, clientPod := range testContext.client.clientPods.Items { + var ( + srcPod = testContext.client.namespace + "/" + clientPod.Name + ) + testContext.client.Header("Validating connectivity from pod %s to the world (google.com)...", srcPod) + _, err := testContext.client.client.ExecInPod(ctx, testContext.client.namespace, clientPod.Name, clientDeploymentName, agnhostConnectCommand("google.com:80")) + if err != nil { + return fmt.Errorf("pod %s was not able to connect to google.com: %w", srcPod, err) + } + testContext.client.Log("Pod %s was able to connect to google.com", srcPod) + } + return nil +} diff --git a/pkg/antctl/raw/check/installation/test_PodToPod.go b/pkg/antctl/raw/check/installation/test_PodToPod.go new file mode 100644 index 00000000000..82f8c8a0247 --- /dev/null +++ b/pkg/antctl/raw/check/installation/test_PodToPod.go @@ -0,0 +1,40 @@ +// Copyright 2024 Antrea 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 installation + +import ( + "context" + "fmt" +) + +type PodtoPodConnectivityTest struct{} + +func (t *PodtoPodConnectivityTest) Run(ctx context.Context, testContext *TestContext) error { + for _, clientPod := range testContext.client.clientPods.Items { + for echoName, echoIP := range testContext.client.echoPods { + var ( + srcPod = testContext.client.namespace + "/" + clientPod.Name + dstPod = testContext.client.namespace + "/" + echoName + ) + testContext.client.Header("Validating from pod %s to pod %s...", srcPod, dstPod) + _, err := testContext.client.client.ExecInPod(ctx, testContext.client.namespace, clientPod.Name, "", agnhostConnectCommand(echoIP+":80")) + if err != nil { + return fmt.Errorf("client pod %s was not able to communicate with echo pod %s (%s): %w", clientPod.Name, echoName, echoIP, err) + } + testContext.client.Log("client pod %s was able to communicate with echo pod %s (%s)", clientPod.Name, echoName, echoIP) + } + } + return nil +}