github.com/tilt-dev/tilt@v0.36.0/integration/tilt_ci_test.go (about)

     1  //go:build integration
     2  // +build integration
     3  
     4  package integration
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"os"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  type localResource struct {
    18  	name        string
    19  	triggerMode string
    20  	autoInit    bool
    21  	updateCmd   string
    22  	serveCmd    string
    23  }
    24  
    25  // TestTiltCI covers a variety of different permutations for local_resource + k8s_resource, in particular around
    26  // auto_init and absence/presence of update cmd/serve_cmd (for local_resource).
    27  //
    28  // Critically, it ensures that `tilt ci` does not block indefinitely; there have been several regressions around
    29  // this due to the subtlety in various states.
    30  func TestTiltCI(t *testing.T) {
    31  	f := newK8sFixture(t, "tilt_ci")
    32  	f.SetRestrictedCredentials()
    33  
    34  	// dynamically generate a bunch of combinations of local_resource args and write out to
    35  	// `Tiltfile.generated` which is loaded in by the main/static Tiltfile (which contains
    36  	// hardcoded K8s definitions as there's fewer combinations there)
    37  	localResources := generateLocalResources()
    38  	generateTiltfile(f.fixture, localResources)
    39  
    40  	f.TiltCI()
    41  
    42  	// NOTE: the assertions don't use assert.Contains because on failure it'll re-print all the logs
    43  	// 	which have already been printed and make reading the test failure very difficult
    44  	logs := f.logs.String()
    45  	for _, lr := range localResources {
    46  		if !lr.autoInit {
    47  			assert.Falsef(t, strings.Contains(logs, lr.name),
    48  				"Resource %q had auto_init=False and should not have been seen", lr.name)
    49  			continue
    50  		}
    51  
    52  		if lr.updateCmd != "" {
    53  			assert.Truef(t, strings.Contains(logs, fmt.Sprintf("update for %s", lr.name)),
    54  				"Resource %q did not log via update_cmd", lr.name)
    55  		}
    56  
    57  		if lr.serveCmd != "" {
    58  			assert.Truef(t, strings.Contains(logs, fmt.Sprintf("serve for %s", lr.name)),
    59  				"Resource %q did not log via serve_cmd", lr.name)
    60  		}
    61  	}
    62  
    63  	for _, name := range []string{"k8s-server-disabled", "k8s-job-disabled"} {
    64  		assert.Falsef(t, strings.Contains(logs, fmt.Sprintf("Initial Build • %s", name)),
    65  			"Resource %q had auto_init=False and should not have been deployed", name)
    66  	}
    67  
    68  	assert.True(t, strings.Contains(logs, "k8s-server-e… │ Initial Build"),
    69  		"Resource k8s-server-enabled did not deploy")
    70  	assert.True(t, strings.Contains(logs, "k8s-job-enab… │ Initial Build"),
    71  		"Resource k8s-job-enabled did not deploy")
    72  
    73  	assert.True(t, strings.Contains(logs, "k8s-job-enabled finished"),
    74  		`Resource "k8s-job-enabled" did not log via container`)
    75  
    76  	assert.True(t, strings.Contains(logs, "k8s-server-enabled running"),
    77  		`Resource "k8s-server-enabled" did not log via container`)
    78  }
    79  
    80  func generateTiltfile(f *fixture, localResources []localResource) {
    81  	var out bytes.Buffer
    82  	out.WriteString("# AUTOMATICALLY GENERATED - DO NOT EDIT\n")
    83  	out.WriteString("# Run TestTiltCI to re-generate\n\n")
    84  
    85  	for _, lr := range localResources {
    86  		out.WriteString(lr.String())
    87  		out.WriteString("\n")
    88  	}
    89  
    90  	err := os.WriteFile(f.testDirPath("Tiltfile.generated"), out.Bytes(), os.FileMode(0777))
    91  	require.NoError(f.t, err, "Failed to write Tiltfile.generated")
    92  }
    93  
    94  func generateLocalResources() []localResource {
    95  	var localResources []localResource
    96  	for _, tm := range []string{"TRIGGER_MODE_AUTO", "TRIGGER_MODE_MANUAL"} {
    97  		for _, autoInit := range []bool{false, true} {
    98  			for _, hasUpdateCmd := range []bool{false, true} {
    99  				for _, hasServeCmd := range []bool{false, true} {
   100  					if !hasUpdateCmd && !hasServeCmd {
   101  						continue
   102  					}
   103  					lr := localResource{triggerMode: tm, autoInit: autoInit}
   104  					lr.name = "local"
   105  					if lr.triggerMode == "TRIGGER_MODE_MANUAL" {
   106  						lr.name += "-trigger_manual"
   107  					}
   108  					if !autoInit {
   109  						lr.name += "-no_auto_init"
   110  					}
   111  					if hasUpdateCmd {
   112  						lr.name += "-update_cmd"
   113  					}
   114  					if hasServeCmd {
   115  						lr.name += "-serve_cmd"
   116  					}
   117  
   118  					// we use the names in the commands so need to finish building the name before setting them
   119  					if hasUpdateCmd {
   120  						lr.updateCmd = fmt.Sprintf(`echo "update for %s"`, lr.name)
   121  					}
   122  					if hasServeCmd {
   123  						lr.serveCmd = fmt.Sprintf(`while true; do echo "serve for %s"; sleep 5000; done`, lr.name)
   124  					}
   125  
   126  					localResources = append(localResources, lr)
   127  				}
   128  			}
   129  		}
   130  	}
   131  	return localResources
   132  }
   133  
   134  func (lr localResource) String() string {
   135  	var args []string
   136  	args = append(args, fmt.Sprintf("name=%q", lr.name))
   137  	var autoInitArgVal string
   138  	if lr.autoInit {
   139  		autoInitArgVal = "True"
   140  	} else {
   141  		autoInitArgVal = "False"
   142  	}
   143  	args = append(args, fmt.Sprintf("auto_init=%s", autoInitArgVal))
   144  	args = append(args, fmt.Sprintf("trigger_mode=%s", lr.triggerMode))
   145  	if lr.updateCmd != "" {
   146  		args = append(args, fmt.Sprintf("cmd=%q", lr.updateCmd))
   147  	}
   148  	if lr.serveCmd != "" {
   149  		args = append(args, fmt.Sprintf("serve_cmd=%q", lr.serveCmd))
   150  	}
   151  
   152  	var out strings.Builder
   153  	out.WriteString(`local_resource(`)
   154  	for i, arg := range args {
   155  		out.WriteString(arg)
   156  		if i != len(args)-1 {
   157  			out.WriteString(", ")
   158  		}
   159  	}
   160  	out.WriteString(")")
   161  	return out.String()
   162  }