github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/build/model.go (about)

     1  /*
     2  Copyright 2020 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 build
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
    23  )
    24  
    25  // node models the artifact dependency graph using a set of channels.
    26  // Each build node has a wait channel which it closes once it completes building by calling markComplete.
    27  // This notifies all listeners waiting for this node's build to complete.
    28  // Additionally it has a reference to the channels for each of its dependencies.
    29  // Calling `waitForDependencies` ensures that all required nodes' channels have already been closed and as such have finished building before the current artifact build starts.
    30  type node struct {
    31  	imageName    string
    32  	wait         chan interface{}
    33  	dependencies []node
    34  }
    35  
    36  // markComplete broadcasts that this node's build is complete.
    37  func (a *node) markComplete() {
    38  	// closing channel notifies all listeners
    39  	close(a.wait)
    40  }
    41  
    42  // waitForDependencies waits for all required builds to complete or returns an error if any build fails
    43  func (a *node) waitForDependencies(ctx context.Context) error {
    44  	for _, dep := range a.dependencies {
    45  		// wait for required builds to complete
    46  		select {
    47  		case <-ctx.Done():
    48  			return ctx.Err()
    49  		case <-dep.wait:
    50  		}
    51  	}
    52  	return nil
    53  }
    54  
    55  func createNodes(artifacts []*latest.Artifact) []node {
    56  	nodeMap := make(map[string]node)
    57  	for _, a := range artifacts {
    58  		nodeMap[a.ImageName] = node{
    59  			imageName: a.ImageName,
    60  			wait:      make(chan interface{}),
    61  		}
    62  	}
    63  
    64  	var nodes []node
    65  	for _, a := range artifacts {
    66  		ar := nodeMap[a.ImageName]
    67  		for _, d := range a.Dependencies {
    68  			ch, found := nodeMap[d.ImageName]
    69  			if !found {
    70  				// if a dependency is not present in `artifacts` slice then we ignore it.
    71  				continue
    72  			}
    73  			ar.dependencies = append(ar.dependencies, ch)
    74  		}
    75  		nodes = append(nodes, ar)
    76  	}
    77  	return nodes
    78  }
    79  
    80  // countingSemaphore uses a buffered channel of size `n` that acts like a counting semaphore, allowing up to `n` concurrent operations
    81  type countingSemaphore struct {
    82  	sem chan bool
    83  }
    84  
    85  func newCountingSemaphore(count int) countingSemaphore {
    86  	return countingSemaphore{sem: make(chan bool, count)}
    87  }
    88  
    89  func (c countingSemaphore) acquire() (release func()) {
    90  	c.sem <- true
    91  	return func() {
    92  		<-c.sem
    93  	}
    94  }