github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/caas/kubernetes/provider/resources/claim.go (about)

     1  // Copyright 2021 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package resources
     5  
     6  import (
     7  	"strings"
     8  
     9  	"github.com/juju/errors"
    10  	"k8s.io/apimachinery/pkg/api/meta"
    11  
    12  	"github.com/juju/juju/caas/kubernetes/provider/constants"
    13  )
    14  
    15  // Claim represents an assertion over a generic Kubernetes object to assert
    16  // ownership. These are used in Juju for cluster scoped resources to assert that
    17  // that Juju is not going to take ownership of an object that was not created by
    18  // itself.
    19  type Claim interface {
    20  	// Assert defines the assertion to run. Returns true if a claim is asserted
    21  	// over the provided object or if an error occurred where a claim can not be
    22  	// made.
    23  	Assert(obj interface{}) (bool, error)
    24  }
    25  
    26  // ClaimFn is a helper type for making Claim types out of functions. See Claim
    27  type ClaimFn func(obj interface{}) (bool, error)
    28  
    29  var (
    30  	// ClaimJujuOwnership asserts that the Kubernetes object has labels that
    31  	// in line with Juju management "ownership".
    32  	ClaimJujuOwnership = ClaimAggregateOr(
    33  		ClaimFn(claimIsManagedByJuju),
    34  		ClaimFn(claimHasJujuLabel),
    35  	)
    36  )
    37  
    38  func (c ClaimFn) Assert(obj interface{}) (bool, error) {
    39  	return c(obj)
    40  }
    41  
    42  // ClaimAggregateOr runs multiple claims looking for the first true condition.
    43  // If no claims are provided or no claim returns true false is returned. The
    44  // first claim to error stops execution.
    45  func ClaimAggregateOr(claims ...Claim) Claim {
    46  	return ClaimFn(func(obj interface{}) (bool, error) {
    47  		for _, claim := range claims {
    48  			if r, err := claim.Assert(obj); err != nil {
    49  				return r, err
    50  			} else if r {
    51  				return r, err
    52  			}
    53  		}
    54  		return false, nil
    55  	})
    56  }
    57  
    58  // claimHasAJujuLabel is a throw everything against the wall and see what sticks
    59  // assertion. It itterates all labels of the object trying to find a key that
    60  // has the lowercase word "juju". We use this because our labeling at one stage
    61  // is a bit hit and miss and no consitancy to fall back on.
    62  // TODO: Remove in Juju 3.0
    63  func claimHasJujuLabel(obj interface{}) (bool, error) {
    64  	if obj == nil {
    65  		return false, errors.NewNotValid(nil, "obj for claim cannot be nil")
    66  	}
    67  
    68  	metaObj, err := meta.Accessor(obj)
    69  	if err != nil {
    70  		return false, errors.Annotate(err, "asserting Kubernetes object has Juju labels")
    71  	}
    72  	for k := range metaObj.GetLabels() {
    73  		if strings.Contains(k, "juju") {
    74  			return true, nil
    75  		}
    76  	}
    77  	return false, nil
    78  }
    79  
    80  // claimIsManagedByJuju is a check to assert that the Kubernetes object provied
    81  // is managed by Juju by having the label key and value of
    82  // app.kubernetes.io/managed-by: juju.
    83  func claimIsManagedByJuju(obj interface{}) (bool, error) {
    84  	if obj == nil {
    85  		return false, errors.NewNotValid(nil, "obj for claim cannot be nil")
    86  	}
    87  
    88  	metaObj, err := meta.Accessor(obj)
    89  	if err != nil {
    90  		return false, errors.Annotate(err, "asserting Kubernetes object has managed by juju label")
    91  	}
    92  
    93  	val, has := metaObj.GetLabels()[constants.LabelKubernetesAppManaged]
    94  	if !has {
    95  		return false, nil
    96  	}
    97  	return val == "juju", nil
    98  }
    99  
   100  // RunClaims runs the provided claims until the first true condition is found or
   101  // the first error occurs. If no claims are provided then true is returned.
   102  func RunClaims(claims ...Claim) Claim {
   103  	return ClaimFn(func(obj interface{}) (bool, error) {
   104  		for _, claim := range claims {
   105  			if r, err := claim.Assert(obj); err != nil {
   106  				return r, err
   107  			} else if r {
   108  				return r, err
   109  			}
   110  		}
   111  		return true, nil
   112  	})
   113  }