github.com/tilt-dev/tilt@v0.36.0/internal/hud/view_test.go (about) 1 package hud 2 3 import ( 4 "path/filepath" 5 "sync" 6 "testing" 7 "time" 8 9 "github.com/google/uuid" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 v1 "k8s.io/api/core/v1" 13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 15 "github.com/tilt-dev/tilt/internal/hud/view" 16 "github.com/tilt-dev/tilt/internal/k8s" 17 "github.com/tilt-dev/tilt/internal/k8s/testyaml" 18 "github.com/tilt-dev/tilt/internal/store" 19 "github.com/tilt-dev/tilt/internal/store/k8sconv" 20 "github.com/tilt-dev/tilt/internal/testutils/manifestbuilder" 21 "github.com/tilt-dev/tilt/internal/testutils/tempdir" 22 "github.com/tilt-dev/tilt/pkg/apis" 23 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 24 "github.com/tilt-dev/tilt/pkg/model" 25 ) 26 27 func TestStateToViewRelativeEditPaths(t *testing.T) { 28 f := tempdir.NewTempDirFixture(t) 29 m := model.Manifest{ 30 Name: "foo", 31 }.WithDeployTarget(model.K8sTarget{}).WithImageTarget(model.ImageTarget{}. 32 WithDockerImage(v1alpha1.DockerImageSpec{Context: f.JoinPath("a", "b", "c")})) 33 34 state := newState([]model.Manifest{m}) 35 ms := state.ManifestTargets[m.Name].State 36 ms.CurrentBuilds["buildcontrol"] = model.BuildRecord{ 37 Edits: []string{ 38 f.JoinPath("a", "b", "c", "foo"), 39 f.JoinPath("a", "b", "c", "d", "e"), 40 }, 41 } 42 ms.BuildHistory = []model.BuildRecord{ 43 { 44 Edits: []string{ 45 f.JoinPath("a", "b", "c", "foo"), 46 f.JoinPath("a", "b", "c", "d", "e"), 47 }, 48 }, 49 } 50 bs, ok := ms.BuildStatus(m.ImageTargets[0].ID()) 51 require.True(t, ok) 52 bs.FileChanges = 53 map[string]time.Time{ 54 f.JoinPath("a", "b", "c", "foo"): time.Now(), 55 f.JoinPath("a", "b", "c", "d", "e"): time.Now(), 56 } 57 v := StateToTerminalView(*state, &sync.RWMutex{}) 58 59 require.Len(t, v.Resources, 2) 60 61 r, _ := v.Resource(m.Name) 62 assert.Equal(t, []string{"foo", filepath.Join("d", "e")}, r.LastBuild().Edits) 63 assert.Equal(t, []string{"foo", filepath.Join("d", "e")}, r.CurrentBuild.Edits) 64 assert.Equal(t, []string{filepath.Join("d", "e"), "foo"}, r.PendingBuildEdits) // these are sorted for deterministic ordering 65 } 66 67 func TestStateToTerminalViewPortForwards(t *testing.T) { 68 m := model.Manifest{ 69 Name: "foo", 70 }.WithDeployTarget(model.K8sTarget{ 71 KubernetesApplySpec: v1alpha1.KubernetesApplySpec{ 72 PortForwardTemplateSpec: &v1alpha1.PortForwardTemplateSpec{ 73 Forwards: []v1alpha1.Forward{ 74 {LocalPort: 8000, ContainerPort: 5000}, 75 {LocalPort: 7000, ContainerPort: 5001}, 76 }, 77 }, 78 }, 79 }) 80 state := newState([]model.Manifest{m}) 81 v := StateToTerminalView(*state, &sync.RWMutex{}) 82 res, _ := v.Resource(m.Name) 83 assert.Equal(t, 84 []string{"http://localhost:8000/", "http://localhost:7000/"}, 85 res.Endpoints) 86 } 87 88 func TestStateToWebViewLinksAndPortForwards(t *testing.T) { 89 m := model.Manifest{ 90 Name: "foo", 91 }.WithDeployTarget(model.K8sTarget{ 92 KubernetesApplySpec: v1alpha1.KubernetesApplySpec{ 93 PortForwardTemplateSpec: &v1alpha1.PortForwardTemplateSpec{ 94 Forwards: []v1alpha1.Forward{ 95 {LocalPort: 8000, ContainerPort: 5000}, 96 {LocalPort: 8001, ContainerPort: 5001, Name: "debugger"}, 97 }, 98 }, 99 }, 100 Links: []model.Link{ 101 model.MustNewLink("www.apple.edu", "apple"), 102 model.MustNewLink("www.zombo.com", "zombo"), 103 }, 104 }) 105 state := newState([]model.Manifest{m}) 106 v := StateToTerminalView(*state, &sync.RWMutex{}) 107 res, _ := v.Resource(m.Name) 108 assert.Equal(t, 109 []string{"www.apple.edu", "www.zombo.com", "http://localhost:8000/", "http://localhost:8001/"}, 110 res.Endpoints) 111 } 112 func TestStateToTerminalViewLocalResourceLinks(t *testing.T) { 113 m := model.Manifest{ 114 Name: "foo", 115 }.WithDeployTarget(model.LocalTarget{ 116 Links: []model.Link{ 117 model.MustNewLink("www.apple.edu", "apple"), 118 model.MustNewLink("www.zombo.com", "zombo"), 119 }, 120 }) 121 state := newState([]model.Manifest{m}) 122 v := StateToTerminalView(*state, &sync.RWMutex{}) 123 res, _ := v.Resource(m.Name) 124 assert.Equal(t, 125 []string{"www.apple.edu", "www.zombo.com"}, 126 res.Endpoints) 127 } 128 129 func TestRuntimeStateNonWorkload(t *testing.T) { 130 f := tempdir.NewTempDirFixture(t) 131 132 m := manifestbuilder.New(f, model.UnresourcedYAMLManifestName). 133 WithK8sYAML(testyaml.SecretYaml). 134 Build() 135 state := newState([]model.Manifest{m}) 136 runtimeState := state.ManifestTargets[m.Name].State.K8sRuntimeState() 137 assert.Equal(t, v1alpha1.RuntimeStatusPending, runtimeState.RuntimeStatus()) 138 139 runtimeState.HasEverDeployedSuccessfully = true 140 141 assert.Equal(t, v1alpha1.RuntimeStatusOK, runtimeState.RuntimeStatus()) 142 } 143 144 func TestRuntimeStateJob(t *testing.T) { 145 for _, tc := range []struct { 146 phase v1.PodPhase 147 expectedRuntimeStatus v1alpha1.RuntimeStatus 148 }{ 149 {v1.PodRunning, v1alpha1.RuntimeStatusPending}, 150 {v1.PodSucceeded, v1alpha1.RuntimeStatusOK}, 151 {v1.PodFailed, v1alpha1.RuntimeStatusError}, 152 } { 153 t.Run(string(tc.phase), func(t *testing.T) { 154 f := tempdir.NewTempDirFixture(t) 155 156 m := manifestbuilder.New(f, "foo"). 157 WithK8sYAML(testyaml.JobYAML). 158 WithK8sPodReadiness(model.PodReadinessSucceeded). 159 Build() 160 state := newState([]model.Manifest{m}) 161 runtimeState := state.ManifestTargets[m.Name].State.K8sRuntimeState() 162 assert.Equal(t, v1alpha1.RuntimeStatusPending, runtimeState.RuntimeStatus()) 163 164 runtimeState.HasEverDeployedSuccessfully = true 165 166 pod := v1alpha1.Pod{ 167 Name: "pod", 168 CreatedAt: apis.Now(), 169 Phase: string(tc.phase), 170 } 171 runtimeState.FilteredPods = []v1alpha1.Pod{pod} 172 173 assert.Equal(t, tc.expectedRuntimeStatus, runtimeState.RuntimeStatus()) 174 }) 175 } 176 } 177 178 func TestRuntimeStateJobCompleteMissingPods(t *testing.T) { 179 f := tempdir.NewTempDirFixture(t) 180 181 m := manifestbuilder.New(f, "foo"). 182 WithK8sYAML(testyaml.JobYAML). 183 WithK8sPodReadiness(model.PodReadinessSucceeded). 184 Build() 185 state := newState([]model.Manifest{m}) 186 runtimeState := state.ManifestTargets[m.Name].State.K8sRuntimeState() 187 runtimeState.HasEverDeployedSuccessfully = true 188 assert.Equal(t, v1alpha1.RuntimeStatusPending, runtimeState.RuntimeStatus()) 189 190 // N.B. there are no pods but a condition attached 191 runtimeState.Conditions = []metav1.Condition{ 192 { 193 Type: v1alpha1.ApplyConditionJobComplete, 194 Status: metav1.ConditionTrue, 195 }, 196 } 197 assert.Equal(t, v1alpha1.RuntimeStatusOK, runtimeState.RuntimeStatus()) 198 } 199 200 func TestStateToTerminalViewUnresourcedYAMLManifest(t *testing.T) { 201 m := k8sManifest(t, model.UnresourcedYAMLManifestName, testyaml.SanchoYAML) 202 state := newState([]model.Manifest{m}) 203 krs := state.ManifestTargets[m.Name].State.K8sRuntimeState() 204 krs.ApplyFilter = yamlToApplyFilter(t, testyaml.SanchoYAML) 205 state.ManifestTargets[m.Name].State.RuntimeState = krs 206 207 v := StateToTerminalView(*state, &sync.RWMutex{}) 208 209 assert.Equal(t, 2, len(v.Resources)) 210 211 r, _ := v.Resource(m.Name) 212 assert.Equal(t, nil, r.LastBuild().Error) 213 214 expectedInfo := view.YAMLResourceInfo{ 215 K8sDisplayNames: []string{"sancho:deployment"}, 216 } 217 assert.Equal(t, expectedInfo, r.ResourceInfo) 218 } 219 220 func TestStateToTerminalViewNonWorkloadYAMLManifest(t *testing.T) { 221 m := k8sManifest(t, "foo", testyaml.SecretYaml) 222 state := newState([]model.Manifest{m}) 223 krs := state.ManifestTargets[m.Name].State.K8sRuntimeState() 224 krs.ApplyFilter = yamlToApplyFilter(t, testyaml.SecretYaml) 225 state.ManifestTargets[m.Name].State.RuntimeState = krs 226 227 v := StateToTerminalView(*state, &sync.RWMutex{}) 228 229 assert.Equal(t, 2, len(v.Resources)) 230 231 r, _ := v.Resource(m.Name) 232 assert.Equal(t, nil, r.LastBuild().Error) 233 234 expectedInfo := view.YAMLResourceInfo{ 235 K8sDisplayNames: []string{"mysecret:secret"}, 236 } 237 assert.Equal(t, expectedInfo, r.ResourceInfo) 238 } 239 240 func newState(manifests []model.Manifest) *store.EngineState { 241 ret := store.NewState() 242 for _, m := range manifests { 243 ret.ManifestTargets[m.Name] = store.NewManifestTarget(m) 244 ret.ManifestDefinitionOrder = append(ret.ManifestDefinitionOrder, m.Name) 245 } 246 247 return ret 248 } 249 250 func yamlToApplyFilter(t testing.TB, yaml string) *k8sconv.KubernetesApplyFilter { 251 t.Helper() 252 entities, err := k8s.ParseYAMLFromString(yaml) 253 require.NoError(t, err, "Failed to parse YAML") 254 for i := range entities { 255 entities[i].SetUID(uuid.New().String()) 256 } 257 yaml, err = k8s.SerializeSpecYAML(entities) 258 require.NoError(t, err, "Failed to re-serialize YAML") 259 applyFilter, err := k8sconv.NewKubernetesApplyFilter(yaml) 260 require.NoError(t, err, "Failed to create KubernetesApplyFilter") 261 require.NotNil(t, applyFilter, "ApplyFilter was nil") 262 return applyFilter 263 } 264 265 func k8sManifest(t testing.TB, name model.ManifestName, yaml string) model.Manifest { 266 t.Helper() 267 kt, err := k8s.NewTargetForYAML(name.TargetName(), yaml, nil) 268 require.NoError(t, err, "Failed to create Kubernetes deploy target") 269 return model.Manifest{Name: name}.WithDeployTarget(kt) 270 }