github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/runner/runcontext/context.go (about)

     1  /*
     2  Copyright 2019 The Skaffold Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package runcontext
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"os"
    23  
    24  	"github.com/google/uuid"
    25  
    26  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
    27  	kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context"
    28  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
    29  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
    30  	schemaUtil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util"
    31  )
    32  
    33  type RunContext struct {
    34  	Opts               config.SkaffoldOptions
    35  	Pipelines          Pipelines
    36  	KubeContext        string
    37  	WorkingDir         string
    38  	InsecureRegistries map[string]bool
    39  	Cluster            config.Cluster
    40  	RunID              string
    41  }
    42  
    43  // Pipelines encapsulates multiple config pipelines
    44  type Pipelines struct {
    45  	pipelines            []latest.Pipeline
    46  	pipelinesByImageName map[string]latest.Pipeline
    47  }
    48  
    49  // All returns all config pipelines.
    50  func (ps Pipelines) All() []latest.Pipeline {
    51  	return ps.pipelines
    52  }
    53  
    54  // Head returns the first `latest.Pipeline`.
    55  func (ps Pipelines) Head() latest.Pipeline {
    56  	return ps.pipelines[0] // there always exists atleast one pipeline.
    57  }
    58  
    59  // Select returns the first `latest.Pipeline` that matches the given artifact `imageName`.
    60  func (ps Pipelines) Select(imageName string) (latest.Pipeline, bool) {
    61  	p, found := ps.pipelinesByImageName[imageName]
    62  	return p, found
    63  }
    64  
    65  // IsMultiPipeline returns true if there are more than one constituent skaffold pipelines.
    66  func (ps Pipelines) IsMultiPipeline() bool {
    67  	return len(ps.pipelines) > 1
    68  }
    69  
    70  func (ps Pipelines) PortForwardResources() []*latest.PortForwardResource {
    71  	var pf []*latest.PortForwardResource
    72  	for _, p := range ps.pipelines {
    73  		pf = append(pf, p.PortForward...)
    74  	}
    75  	return pf
    76  }
    77  
    78  func (ps Pipelines) Artifacts() []*latest.Artifact {
    79  	var artifacts []*latest.Artifact
    80  	for _, p := range ps.pipelines {
    81  		artifacts = append(artifacts, p.Build.Artifacts...)
    82  	}
    83  	return artifacts
    84  }
    85  
    86  func (ps Pipelines) DeployConfigs() []latest.DeployConfig {
    87  	var cfgs []latest.DeployConfig
    88  	for _, p := range ps.pipelines {
    89  		cfgs = append(cfgs, p.Deploy)
    90  	}
    91  	return cfgs
    92  }
    93  
    94  func (ps Pipelines) Deployers() []latest.DeployConfig {
    95  	var deployers []latest.DeployConfig
    96  	for _, p := range ps.pipelines {
    97  		deployers = append(deployers, p.Deploy)
    98  	}
    99  	return deployers
   100  }
   101  
   102  func (ps Pipelines) TestCases() []*latest.TestCase {
   103  	var tests []*latest.TestCase
   104  	for _, p := range ps.pipelines {
   105  		tests = append(tests, p.Test...)
   106  	}
   107  	return tests
   108  }
   109  
   110  // TransformAllowList returns combined allowlist from pipelines
   111  func (ps Pipelines) TransformAllowList() []latest.ResourceFilter {
   112  	var allowlist []latest.ResourceFilter
   113  	for _, p := range ps.pipelines {
   114  		if p.ResourceSelector.Allow != nil {
   115  			allowlist = append(allowlist, p.ResourceSelector.Allow...)
   116  		}
   117  	}
   118  	return allowlist
   119  }
   120  
   121  // TransformDenyList returns combined denylist from pipelines
   122  func (ps Pipelines) TransformDenyList() []latest.ResourceFilter {
   123  	var denylist []latest.ResourceFilter
   124  	for _, p := range ps.pipelines {
   125  		if p.ResourceSelector.Deny != nil {
   126  			denylist = append(denylist, p.ResourceSelector.Deny...)
   127  		}
   128  	}
   129  	return denylist
   130  }
   131  
   132  func (ps Pipelines) StatusCheckDeadlineSeconds() int {
   133  	c := 0
   134  	// set the group status check deadline to maximum of any individually specified value
   135  	for _, p := range ps.pipelines {
   136  		if p.Deploy.StatusCheckDeadlineSeconds > c {
   137  			c = p.Deploy.StatusCheckDeadlineSeconds
   138  		}
   139  	}
   140  	return c
   141  }
   142  func NewPipelines(pipelines []latest.Pipeline) Pipelines {
   143  	m := make(map[string]latest.Pipeline)
   144  	for _, p := range pipelines {
   145  		for _, a := range p.Build.Artifacts {
   146  			m[a.ImageName] = p
   147  		}
   148  	}
   149  	return Pipelines{pipelines: pipelines, pipelinesByImageName: m}
   150  }
   151  
   152  func (rc *RunContext) PipelineForImage(imageName string) (latest.Pipeline, bool) {
   153  	return rc.Pipelines.Select(imageName)
   154  }
   155  
   156  func (rc *RunContext) PortForwardResources() []*latest.PortForwardResource {
   157  	return rc.Pipelines.PortForwardResources()
   158  }
   159  
   160  func (rc *RunContext) Artifacts() []*latest.Artifact { return rc.Pipelines.Artifacts() }
   161  
   162  func (rc *RunContext) DeployConfigs() []latest.DeployConfig { return rc.Pipelines.DeployConfigs() }
   163  
   164  func (rc *RunContext) Deployers() []latest.DeployConfig { return rc.Pipelines.Deployers() }
   165  
   166  func (rc *RunContext) TestCases() []*latest.TestCase { return rc.Pipelines.TestCases() }
   167  
   168  func (rc *RunContext) StatusCheckDeadlineSeconds() int {
   169  	return rc.Pipelines.StatusCheckDeadlineSeconds()
   170  }
   171  
   172  func (rc *RunContext) SkipTests() bool {
   173  	return rc.Opts.SkipTests
   174  }
   175  
   176  func (rc *RunContext) IsTestPhaseActive() bool {
   177  	return !rc.SkipTests() && len(rc.TestCases()) != 0
   178  }
   179  
   180  func (rc *RunContext) TransformAllowList() []latest.ResourceFilter {
   181  	return rc.Pipelines.TransformAllowList()
   182  }
   183  
   184  func (rc *RunContext) TransformDenyList() []latest.ResourceFilter {
   185  	return rc.Pipelines.TransformDenyList()
   186  }
   187  
   188  // AddSkaffoldLabels tells the Runner whether to add skaffold-specific labels.
   189  // We only ever skip adding labels during a `skaffold render`.
   190  func (rc *RunContext) AddSkaffoldLabels() bool {
   191  	return rc.Opts.Mode() != config.RunModes.Render
   192  }
   193  
   194  func (rc *RunContext) DefaultPipeline() latest.Pipeline              { return rc.Pipelines.Head() }
   195  func (rc *RunContext) GetKubeContext() string                        { return rc.KubeContext }
   196  func (rc *RunContext) GetPipelines() []latest.Pipeline               { return rc.Pipelines.All() }
   197  func (rc *RunContext) GetInsecureRegistries() map[string]bool        { return rc.InsecureRegistries }
   198  func (rc *RunContext) GetWorkingDir() string                         { return rc.WorkingDir }
   199  func (rc *RunContext) GetCluster() config.Cluster                    { return rc.Cluster }
   200  func (rc *RunContext) GetNamespace() string                          { return rc.Opts.Namespace }
   201  func (rc *RunContext) AutoBuild() bool                               { return rc.Opts.AutoBuild }
   202  func (rc *RunContext) AutoDeploy() bool                              { return rc.Opts.AutoDeploy }
   203  func (rc *RunContext) AutoSync() bool                                { return rc.Opts.AutoSync }
   204  func (rc *RunContext) ContainerDebugging() bool                      { return rc.Opts.ContainerDebugging }
   205  func (rc *RunContext) CacheArtifacts() bool                          { return rc.Opts.CacheArtifacts }
   206  func (rc *RunContext) CacheFile() string                             { return rc.Opts.CacheFile }
   207  func (rc *RunContext) ConfigurationFile() string                     { return rc.Opts.ConfigurationFile }
   208  func (rc *RunContext) CustomLabels() []string                        { return rc.Opts.CustomLabels }
   209  func (rc *RunContext) CustomTag() string                             { return rc.Opts.CustomTag }
   210  func (rc *RunContext) DefaultRepo() *string                          { return rc.Cluster.DefaultRepo.Value() }
   211  func (rc *RunContext) MultiLevelRepo() *bool                         { return rc.Opts.MultiLevelRepo }
   212  func (rc *RunContext) Mode() config.RunMode                          { return rc.Opts.Mode() }
   213  func (rc *RunContext) DigestSource() string                          { return rc.Opts.DigestSource }
   214  func (rc *RunContext) DryRun() bool                                  { return rc.Opts.DryRun }
   215  func (rc *RunContext) ForceDeploy() bool                             { return rc.Opts.Force }
   216  func (rc *RunContext) GetKubeConfig() string                         { return rc.Opts.KubeConfig }
   217  func (rc *RunContext) GetKubeNamespace() string                      { return rc.Opts.Namespace }
   218  func (rc *RunContext) GlobalConfig() string                          { return rc.Opts.GlobalConfig }
   219  func (rc *RunContext) HydratedManifests() []string                   { return rc.Opts.HydratedManifests }
   220  func (rc *RunContext) LoadImages() bool                              { return rc.Cluster.LoadImages }
   221  func (rc *RunContext) ForceLoadImages() bool                         { return rc.Opts.ForceLoadImages }
   222  func (rc *RunContext) MinikubeProfile() string                       { return rc.Opts.MinikubeProfile }
   223  func (rc *RunContext) Muted() config.Muted                           { return rc.Opts.Muted }
   224  func (rc *RunContext) NoPruneChildren() bool                         { return rc.Opts.NoPruneChildren }
   225  func (rc *RunContext) Notification() bool                            { return rc.Opts.Notification }
   226  func (rc *RunContext) PortForward() bool                             { return rc.Opts.PortForward.Enabled() }
   227  func (rc *RunContext) PortForwardOptions() config.PortForwardOptions { return rc.Opts.PortForward }
   228  func (rc *RunContext) Prune() bool                                   { return rc.Opts.Prune() }
   229  func (rc *RunContext) RenderOnly() bool                              { return rc.Opts.RenderOnly }
   230  func (rc *RunContext) RenderOutput() string                          { return rc.Opts.RenderOutput }
   231  func (rc *RunContext) SkipRender() bool                              { return rc.Opts.SkipRender }
   232  func (rc *RunContext) StatusCheck() *bool                            { return rc.Opts.StatusCheck.Value() }
   233  func (rc *RunContext) IterativeStatusCheck() bool                    { return rc.Opts.IterativeStatusCheck }
   234  func (rc *RunContext) Tail() bool                                    { return rc.Opts.Tail }
   235  func (rc *RunContext) Trigger() string                               { return rc.Opts.Trigger }
   236  func (rc *RunContext) WaitForDeletions() config.WaitForDeletions     { return rc.Opts.WaitForDeletions }
   237  func (rc *RunContext) WatchPollInterval() int                        { return rc.Opts.WatchPollInterval }
   238  func (rc *RunContext) BuildConcurrency() int                         { return rc.Opts.BuildConcurrency }
   239  func (rc *RunContext) IsMultiConfig() bool                           { return rc.Pipelines.IsMultiPipeline() }
   240  func (rc *RunContext) IsDefaultKubeContext() bool                    { return rc.Opts.KubeContext == "" }
   241  func (rc *RunContext) GetRunID() string                              { return rc.RunID }
   242  func (rc *RunContext) RPCPort() *int                                 { return rc.Opts.RPCPort.Value() }
   243  func (rc *RunContext) RPCHTTPPort() *int                             { return rc.Opts.RPCHTTPPort.Value() }
   244  func (rc *RunContext) PushImages() config.BoolOrUndefined            { return rc.Opts.PushImages }
   245  func (rc *RunContext) TransformRulesFile() string                    { return rc.Opts.TransformRulesFile }
   246  func (rc *RunContext) JSONParseConfig() latest.JSONParseConfig {
   247  	return rc.DefaultPipeline().Deploy.Logs.JSONParse
   248  }
   249  
   250  func GetRunContext(ctx context.Context, opts config.SkaffoldOptions, configs []schemaUtil.VersionedConfig) (*RunContext, error) {
   251  	var pipelines []latest.Pipeline
   252  	for _, cfg := range configs {
   253  		if cfg != nil {
   254  			pipelines = append(pipelines, cfg.(*latest.SkaffoldConfig).Pipeline)
   255  		}
   256  	}
   257  	kubeConfig, err := kubectx.CurrentConfig()
   258  	if err != nil {
   259  		return nil, fmt.Errorf("getting current cluster context: %w", err)
   260  	}
   261  	kubeContext := kubeConfig.CurrentContext
   262  	log.Entry(context.TODO()).Infof("Using kubectl context: %s", kubeContext)
   263  
   264  	// TODO(dgageot): this should be the folder containing skaffold.yaml. Should also be moved elsewhere.
   265  	cwd, err := os.Getwd()
   266  	if err != nil {
   267  		return nil, fmt.Errorf("finding current directory: %w", err)
   268  	}
   269  
   270  	// combine all provided lists of insecure registries into a map
   271  	cfgRegistries, err := config.GetInsecureRegistries(opts.GlobalConfig)
   272  	if err != nil {
   273  		log.Entry(context.TODO()).Warn("error retrieving insecure registries from global config: push/pull issues may exist...")
   274  	}
   275  	var regList []string
   276  	regList = append(regList, opts.InsecureRegistries...)
   277  	for _, cfg := range pipelines {
   278  		regList = append(regList, cfg.Build.InsecureRegistries...)
   279  	}
   280  	regList = append(regList, cfgRegistries...)
   281  	insecureRegistries := make(map[string]bool, len(regList))
   282  	for _, r := range regList {
   283  		insecureRegistries[r] = true
   284  	}
   285  	ps := NewPipelines(pipelines)
   286  
   287  	// TODO(https://github.com/GoogleContainerTools/skaffold/issues/3668):
   288  	// remove minikubeProfile from here and instead detect it by matching the
   289  	// kubecontext API Server to minikube profiles
   290  	cluster, err := config.GetCluster(ctx, config.GetClusterOpts{
   291  		ConfigFile:      opts.GlobalConfig,
   292  		DefaultRepo:     opts.DefaultRepo,
   293  		MinikubeProfile: opts.MinikubeProfile,
   294  		DetectMinikube:  opts.DetectMinikube,
   295  	})
   296  	if err != nil {
   297  		return nil, fmt.Errorf("getting cluster: %w", err)
   298  	}
   299  
   300  	runID := uuid.New().String()
   301  
   302  	return &RunContext{
   303  		Opts:               opts,
   304  		Pipelines:          ps,
   305  		WorkingDir:         cwd,
   306  		KubeContext:        kubeContext,
   307  		InsecureRegistries: insecureRegistries,
   308  		Cluster:            cluster,
   309  		RunID:              runID,
   310  	}, nil
   311  }