github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/cli/enable.go (about) 1 package cli 2 3 import ( 4 "context" 5 "fmt" 6 "strconv" 7 "time" 8 9 "github.com/pkg/errors" 10 "github.com/spf13/cobra" 11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 "sigs.k8s.io/controller-runtime/pkg/client" 13 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 14 15 "github.com/tilt-dev/tilt/internal/analytics" 16 engineanalytics "github.com/tilt-dev/tilt/internal/engine/analytics" 17 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 18 "github.com/tilt-dev/tilt/pkg/model" 19 ) 20 21 type enableCmd struct { 22 all bool 23 only bool 24 labels []string 25 } 26 27 func newEnableCmd() *enableCmd { 28 return &enableCmd{} 29 } 30 31 func (c *enableCmd) name() model.TiltSubcommand { return "enable" } 32 33 func (c *enableCmd) register() *cobra.Command { 34 cmd := &cobra.Command{ 35 Use: "enable {-all | [--only] <resource>...}", 36 DisableFlagsInUseLine: true, 37 Short: "Enables resources", 38 Long: `Enables the specified resources in Tilt. 39 40 # enables the resources named 'frontend' and 'backend' 41 tilt enable frontend backend 42 43 # enables frontend and backend and disables all others 44 tilt enable --only frontend backend 45 46 # enables all resources 47 tilt enable --all 48 `, 49 } 50 51 addConnectServerFlags(cmd) 52 cmd.Flags().StringSliceVarP(&c.labels, "labels", "l", c.labels, "Enable all resources with the specified labels") 53 cmd.Flags().BoolVar(&c.only, "only", false, "Enable the specified resources, disable all others") 54 cmd.Flags().BoolVar(&c.all, "all", false, "Enable all resources") 55 56 return cmd 57 } 58 59 func (c *enableCmd) run(ctx context.Context, args []string) error { 60 ctrlclient, err := newClient(ctx) 61 if err != nil { 62 return err 63 } 64 65 if c.all { 66 if c.only { 67 return errors.New("cannot use --all with --only") 68 } else if len(args) > 0 { 69 return errors.New("cannot use --all with resource names") 70 } 71 } else if len(args) == 0 && len(c.labels) == 0 { 72 return errors.New("must specify at least one resource") 73 } 74 75 a := analytics.Get(ctx) 76 cmdTags := engineanalytics.CmdTags(map[string]string{}) 77 cmdTags["only"] = strconv.FormatBool(c.only) 78 cmdTags["all"] = strconv.FormatBool(c.all) 79 a.Incr("cmd.enable", cmdTags.AsMap()) 80 defer a.Flush(time.Second) 81 82 names := make(map[string]bool) 83 for _, name := range args { 84 names[name] = true 85 } 86 87 err = changeEnabledResources(ctx, ctrlclient, args, enableOptions{enable: true, all: c.all, only: c.only, labels: c.labels}) 88 if err != nil { 89 return err 90 } 91 92 return nil 93 } 94 95 type enableOptions struct { 96 enable bool 97 all bool 98 only bool 99 labels []string 100 } 101 102 // Changes which resources are enabled in Tilt. 103 // For resources in `selectedResources`, enable them if `opts.enable` is true, else disable them. 104 // If `opts.only` is true, enable/disable `selectedResources` as above, and do the opposite to all other resources. 105 // If `opts.all` is true, ignore `selectedResources` and act on all resources. 106 func changeEnabledResources( 107 ctx context.Context, 108 cli client.Client, 109 selectedResources []string, 110 opts enableOptions) error { 111 var uirs v1alpha1.UIResourceList 112 err := cli.List(ctx, &uirs) 113 if err != nil { 114 return err 115 } 116 117 // before making any changes, validate that all selected names actually exist 118 uirByName := make(map[string]v1alpha1.UIResource) 119 for _, uir := range uirs.Items { 120 uirByName[uir.Name] = uir 121 } 122 selectedResourcesByName := make(map[string]bool) 123 for _, name := range selectedResources { 124 uir, ok := uirByName[name] 125 if !ok { 126 return fmt.Errorf("no such resource %q", name) 127 } 128 if len(uir.Status.DisableStatus.Sources) == 0 { 129 return fmt.Errorf("%s cannot be enabled or disabled", name) 130 } 131 selectedResourcesByName[name] = true 132 } 133 134 for _, uir := range uirs.Items { 135 // resources w/o disable sources are always enabled (e.g., (Tiltfile)) 136 if len(uir.Status.DisableStatus.Sources) == 0 { 137 continue 138 } 139 140 var enable bool 141 if selectedResourcesByName[uir.Name] { 142 enable = opts.enable 143 } else if len(opts.labels) > 0 { 144 var hasLabel bool 145 for _, label := range opts.labels { 146 if _, hasLabel = uir.Labels[label]; hasLabel { 147 enable = opts.enable 148 break 149 } 150 } 151 if !hasLabel { 152 continue 153 } 154 } else if opts.all { 155 enable = opts.enable 156 } else if opts.only { 157 enable = !opts.enable 158 } else { 159 continue 160 } 161 162 for _, source := range uir.Status.DisableStatus.Sources { 163 if source.ConfigMap == nil { 164 return fmt.Errorf("internal error: resource %s's DisableSource does not have a ConfigMap'", uir.Name) 165 } 166 cm := &v1alpha1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: source.ConfigMap.Name}} 167 _, err := controllerutil.CreateOrUpdate(ctx, cli, cm, func() error { 168 if cm.Data == nil { 169 cm.Data = make(map[string]string) 170 } 171 cm.Data[source.ConfigMap.Key] = strconv.FormatBool(!enable) 172 return nil 173 }) 174 if err != nil { 175 return err 176 } 177 } 178 } 179 180 return nil 181 }