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  }