github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/hooks/sync_test.go (about)

     1  /*
     2  Copyright 2021 The Skaffold Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package hooks
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"fmt"
    23  	"io"
    24  	"path/filepath"
    25  	"strings"
    26  	"testing"
    27  
    28  	corev1 "k8s.io/api/core/v1"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/client-go/kubernetes"
    31  	fakeclient "k8s.io/client-go/kubernetes/fake"
    32  
    33  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl"
    34  	kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client"
    35  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/log"
    36  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
    37  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
    38  	"github.com/GoogleContainerTools/skaffold/testutil"
    39  )
    40  
    41  func TestSyncHooks(t *testing.T) {
    42  	testutil.Run(t, "TestSyncHooks", func(t *testutil.T) {
    43  		workDir, _ := filepath.Abs("./foo")
    44  		hooks := latest.SyncHooks{
    45  			PreHooks: []latest.SyncHookItem{
    46  				{
    47  					HostHook: &latest.HostHook{
    48  						OS:      []string{"linux", "darwin"},
    49  						Command: []string{"sh", "-c", "echo pre-hook running with SKAFFOLD_IMAGE=$SKAFFOLD_IMAGE,SKAFFOLD_BUILD_CONTEXT=$SKAFFOLD_BUILD_CONTEXT,SKAFFOLD_FILES_ADDED_OR_MODIFIED=$SKAFFOLD_FILES_ADDED_OR_MODIFIED,SKAFFOLD_FILES_DELETED=$SKAFFOLD_FILES_DELETED,SKAFFOLD_KUBE_CONTEXT=$SKAFFOLD_KUBE_CONTEXT,SKAFFOLD_NAMESPACES=$SKAFFOLD_NAMESPACES"},
    50  					},
    51  				},
    52  				{
    53  					HostHook: &latest.HostHook{
    54  						OS:      []string{"windows"},
    55  						Command: []string{"cmd.exe", "/C", "echo pre-hook running with SKAFFOLD_IMAGE=%SKAFFOLD_IMAGE%,SKAFFOLD_BUILD_CONTEXT=%SKAFFOLD_BUILD_CONTEXT%,SKAFFOLD_FILES_ADDED_OR_MODIFIED=%SKAFFOLD_FILES_ADDED_OR_MODIFIED%,SKAFFOLD_FILES_DELETED=%SKAFFOLD_FILES_DELETED%,SKAFFOLD_KUBE_CONTEXT=%SKAFFOLD_KUBE_CONTEXT%,SKAFFOLD_NAMESPACES=%SKAFFOLD_NAMESPACES%"},
    56  					},
    57  				},
    58  				{
    59  					ContainerHook: &latest.ContainerHook{
    60  						Command: []string{"foo", "pre-hook"},
    61  					},
    62  				},
    63  			},
    64  			PostHooks: []latest.SyncHookItem{
    65  				{
    66  					HostHook: &latest.HostHook{
    67  						OS:      []string{"linux", "darwin"},
    68  						Command: []string{"sh", "-c", "echo post-hook running with SKAFFOLD_IMAGE=$SKAFFOLD_IMAGE,SKAFFOLD_BUILD_CONTEXT=$SKAFFOLD_BUILD_CONTEXT,SKAFFOLD_FILES_ADDED_OR_MODIFIED=$SKAFFOLD_FILES_ADDED_OR_MODIFIED,SKAFFOLD_FILES_DELETED=$SKAFFOLD_FILES_DELETED,SKAFFOLD_KUBE_CONTEXT=$SKAFFOLD_KUBE_CONTEXT,SKAFFOLD_NAMESPACES=$SKAFFOLD_NAMESPACES"},
    69  					},
    70  				},
    71  				{
    72  					HostHook: &latest.HostHook{
    73  						OS:      []string{"windows"},
    74  						Command: []string{"cmd.exe", "/C", "echo post-hook running with SKAFFOLD_IMAGE=%SKAFFOLD_IMAGE%,SKAFFOLD_BUILD_CONTEXT=%SKAFFOLD_BUILD_CONTEXT%,SKAFFOLD_FILES_ADDED_OR_MODIFIED=%SKAFFOLD_FILES_ADDED_OR_MODIFIED%,SKAFFOLD_FILES_DELETED=%SKAFFOLD_FILES_DELETED%,SKAFFOLD_KUBE_CONTEXT=%SKAFFOLD_KUBE_CONTEXT%,SKAFFOLD_NAMESPACES=%SKAFFOLD_NAMESPACES%"},
    75  					},
    76  				},
    77  				{
    78  					ContainerHook: &latest.ContainerHook{
    79  						Command: []string{"foo", "post-hook"},
    80  					},
    81  				},
    82  			},
    83  		}
    84  		preHostHookOut := fmt.Sprintf("pre-hook running with SKAFFOLD_IMAGE=gcr.io/foo/img1:latest,SKAFFOLD_BUILD_CONTEXT=%s,SKAFFOLD_FILES_ADDED_OR_MODIFIED=foo1;bar1,SKAFFOLD_FILES_DELETED=foo2;bar2,SKAFFOLD_KUBE_CONTEXT=context1,SKAFFOLD_NAMESPACES=np1,np2", workDir)
    85  		preContainerHookOut := "container pre-hook succeeded"
    86  		postHostHookOut := fmt.Sprintf("post-hook running with SKAFFOLD_IMAGE=gcr.io/foo/img1:latest,SKAFFOLD_BUILD_CONTEXT=%s,SKAFFOLD_FILES_ADDED_OR_MODIFIED=foo1;bar1,SKAFFOLD_FILES_DELETED=foo2;bar2,SKAFFOLD_KUBE_CONTEXT=context1,SKAFFOLD_NAMESPACES=np1,np2", workDir)
    87  		postContainerHookOut := "container post-hook succeeded"
    88  
    89  		artifact := &latest.Artifact{
    90  			ImageName: "img1",
    91  			Workspace: workDir,
    92  			Sync: &latest.Sync{
    93  				LifecycleHooks: hooks,
    94  			},
    95  		}
    96  		image := "gcr.io/foo/img1:latest"
    97  		namespaces := []string{"np1", "np2"}
    98  		kubeContext := "context1"
    99  		opts, err := NewSyncEnvOpts(artifact, image, []string{"foo1", "bar1"}, []string{"foo2", "bar2"}, namespaces, kubeContext)
   100  		t.CheckNoError(err)
   101  		formatter := func(corev1.Pod, corev1.ContainerStatus, func() bool) log.Formatter { return mockLogFormatter{} }
   102  		runner := NewSyncRunner(&kubectl.CLI{KubeContext: kubeContext}, artifact.ImageName, image, namespaces, formatter, artifact.Sync.LifecycleHooks, opts)
   103  
   104  		t.Override(&util.DefaultExecCommand,
   105  			testutil.CmdRunWithOutput("kubectl --context context1 exec pod1 --namespace np1 -c container1 -- foo pre-hook", preContainerHookOut).
   106  				AndRunWithOutput("kubectl --context context1 exec pod1 --namespace np1 -c container1 -- foo post-hook", postContainerHookOut))
   107  		t.Override(&kubernetesclient.Client, fakeKubernetesClient)
   108  		var preOut, postOut bytes.Buffer
   109  		err = runner.RunPreHooks(context.Background(), &preOut)
   110  		t.CheckNoError(err)
   111  		t.CheckContains(preHostHookOut, preOut.String())
   112  		t.CheckContains(preContainerHookOut, strings.TrimRight(preOut.String(), "\r\n"))
   113  		err = runner.RunPostHooks(context.Background(), &postOut)
   114  		t.CheckNoError(err)
   115  		t.CheckContains(postHostHookOut, postOut.String())
   116  		t.CheckContains(postContainerHookOut, postOut.String())
   117  	})
   118  }
   119  
   120  func fakeKubernetesClient(string) (kubernetes.Interface, error) {
   121  	pod := &corev1.Pod{
   122  		ObjectMeta: metav1.ObjectMeta{
   123  			Name:      "pod1",
   124  			Namespace: "np1",
   125  			OwnerReferences: []metav1.OwnerReference{
   126  				{
   127  					Name: "rs",
   128  					Kind: "ReplicaSet",
   129  				},
   130  			},
   131  		},
   132  		Status: corev1.PodStatus{Phase: corev1.PodRunning},
   133  		Spec: corev1.PodSpec{Containers: []corev1.Container{
   134  			{
   135  				Name:  "container1",
   136  				Image: "gcr.io/foo/img1:latest",
   137  			},
   138  		}},
   139  	}
   140  	return fakeclient.NewSimpleClientset(pod), nil
   141  }
   142  
   143  type mockLogFormatter struct{}
   144  
   145  func (mockLogFormatter) Name() string { return "" }
   146  
   147  func (mockLogFormatter) PrintLine(w io.Writer, s string) { fmt.Fprint(w, s) }