github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/tiltfile/k8scontext/k8scontext.go (about)

     1  package k8scontext
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"go.starlark.net/starlark"
     7  
     8  	"github.com/tilt-dev/clusterid"
     9  	"github.com/tilt-dev/tilt/internal/k8s"
    10  	"github.com/tilt-dev/tilt/internal/tiltfile/starkit"
    11  	"github.com/tilt-dev/tilt/internal/tiltfile/value"
    12  	"github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1"
    13  	"github.com/tilt-dev/tilt/pkg/model"
    14  )
    15  
    16  // Implements functions for dealing with the Kubernetes context.
    17  // Exposes an API for other plugins to get and validate the allowed k8s context.
    18  type Plugin struct {
    19  	context   k8s.KubeContext
    20  	namespace k8s.Namespace
    21  	env       clusterid.Product
    22  }
    23  
    24  func NewPlugin(context k8s.KubeContext, namespace k8s.Namespace, env clusterid.Product) Plugin {
    25  	return Plugin{
    26  		context:   context,
    27  		namespace: namespace,
    28  		env:       env,
    29  	}
    30  }
    31  
    32  func (e Plugin) NewState() interface{} {
    33  	return State{context: e.context, env: e.env}
    34  }
    35  
    36  func (e Plugin) OnStart(env *starkit.Environment) error {
    37  	err := env.AddBuiltin("allow_k8s_contexts", e.allowK8sContexts)
    38  	if err != nil {
    39  		return err
    40  	}
    41  
    42  	err = env.AddBuiltin("k8s_context", e.k8sContext)
    43  	if err != nil {
    44  		return err
    45  	}
    46  
    47  	err = env.AddBuiltin("k8s_namespace", e.k8sNamespace)
    48  	if err != nil {
    49  		return err
    50  	}
    51  	return nil
    52  }
    53  
    54  func (e Plugin) k8sContext(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
    55  	return starlark.String(e.context), nil
    56  }
    57  
    58  func (e Plugin) k8sNamespace(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
    59  	return starlark.String(e.namespace), nil
    60  }
    61  
    62  func (e Plugin) allowK8sContexts(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
    63  	var contexts starlark.Value
    64  	if err := starkit.UnpackArgs(thread, fn.Name(), args, kwargs,
    65  		"contexts", &contexts,
    66  	); err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	newContexts := []k8s.KubeContext{}
    71  	for _, c := range value.ValueOrSequenceToSlice(contexts) {
    72  		switch val := c.(type) {
    73  		case starlark.String:
    74  			newContexts = append(newContexts, k8s.KubeContext(val))
    75  		default:
    76  			return nil, fmt.Errorf("allow_k8s_contexts contexts must be a string or a sequence of strings; found a %T", val)
    77  
    78  		}
    79  	}
    80  
    81  	err := starkit.SetState(thread, func(existing State) State {
    82  		return State{
    83  			context: existing.context,
    84  			env:     existing.env,
    85  			allowed: append(newContexts, existing.allowed...),
    86  		}
    87  	})
    88  
    89  	return starlark.None, err
    90  }
    91  
    92  var _ starkit.StatefulPlugin = &Plugin{}
    93  
    94  type State struct {
    95  	context k8s.KubeContext
    96  	env     clusterid.Product
    97  	allowed []k8s.KubeContext
    98  }
    99  
   100  func (s State) KubeContext() k8s.KubeContext {
   101  	return s.context
   102  }
   103  
   104  // Returns whether we're allowed to deploy to this kubecontext.
   105  //
   106  // Checks against a manually specified list and a baked-in list
   107  // with known dev cluster names.
   108  //
   109  // Currently, only the tiltfile executor knows about "allowed" kubecontexts.
   110  //
   111  // We don't keep this information around after tiltfile execution finishes.
   112  //
   113  // This is incompatible with the overall technical direction of tilt as an
   114  // apiserver.  Objects registered via the API (like KubernetesApplys) don't get
   115  // this protection. And it's currently only limited to the main Tiltfile.
   116  //
   117  // A more compatible solution would be to have api server objects
   118  // for the kubecontexts that tilt is aware of, and ways to mark them safe.
   119  func (s State) IsAllowed(tf *v1alpha1.Tiltfile) bool {
   120  	if tf.Name != model.MainTiltfileManifestName.String() {
   121  		return true
   122  	}
   123  
   124  	if s.env == k8s.ProductNone || s.env.IsDevCluster() {
   125  		return true
   126  	}
   127  
   128  	for _, c := range s.allowed {
   129  		if c == s.context {
   130  			return true
   131  		}
   132  	}
   133  
   134  	return false
   135  }
   136  
   137  func MustState(model starkit.Model) State {
   138  	state, err := GetState(model)
   139  	if err != nil {
   140  		panic(err)
   141  	}
   142  	return state
   143  }
   144  
   145  func GetState(model starkit.Model) (State, error) {
   146  	var state State
   147  	err := model.Load(&state)
   148  	return state, err
   149  }