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