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

     1  // Copyright 2021 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package specs
     5  
     6  import (
     7  	"encoding/json"
     8  
     9  	"github.com/juju/errors"
    10  	networkingv1 "k8s.io/api/networking/v1"
    11  	networkingv1beta1 "k8s.io/api/networking/v1beta1"
    12  	"k8s.io/apimachinery/pkg/util/intstr"
    13  )
    14  
    15  const (
    16  	// K8sIngressV1Beta1 defines the v1beta1 API version for ingress.
    17  	K8sIngressV1Beta1 APIVersion = "v1beta1"
    18  
    19  	// K8sIngressV1 defines the v1 API version for ingress.
    20  	K8sIngressV1 APIVersion = "v1"
    21  )
    22  
    23  // K8sIngressSpec defines the spec details of the Ingress with the API version.
    24  type K8sIngressSpec struct {
    25  	Version     APIVersion
    26  	SpecV1Beta1 networkingv1beta1.IngressSpec
    27  	SpecV1      networkingv1.IngressSpec
    28  }
    29  
    30  // UnmarshalJSON implements the json.Unmarshaller interface.
    31  func (ing *K8sIngressSpec) UnmarshalJSON(value []byte) (err error) {
    32  	err = unmarshalJSONStrict(value, &ing.SpecV1)
    33  	if err == nil {
    34  		ing.Version = K8sIngressV1
    35  		return nil
    36  	}
    37  	if err2 := unmarshalJSONStrict(value, &ing.SpecV1Beta1); err2 == nil {
    38  		ing.Version = K8sIngressV1Beta1
    39  		return nil
    40  	}
    41  	return errors.Trace(err)
    42  }
    43  
    44  // MarshalJSON implements the json.Marshaller interface.
    45  func (ing K8sIngressSpec) MarshalJSON() ([]byte, error) {
    46  	switch ing.Version {
    47  	case K8sIngressV1Beta1:
    48  		return json.Marshal(ing.SpecV1Beta1)
    49  	case K8sIngressV1:
    50  		return json.Marshal(ing.SpecV1)
    51  	default:
    52  		return []byte{}, errors.NotSupportedf("ingress version %q", ing.Version)
    53  	}
    54  }
    55  
    56  // K8sIngress defines spec for creating or updating an ingress resource.
    57  type K8sIngress struct {
    58  	Meta `json:",inline" yaml:",inline"`
    59  	Spec K8sIngressSpec `json:"spec" yaml:"spec"`
    60  }
    61  
    62  // Validate returns an error if the spec is not valid.
    63  func (ing K8sIngress) Validate() error {
    64  	if err := ing.Meta.Validate(); err != nil {
    65  		return errors.Trace(err)
    66  	}
    67  	return nil
    68  }
    69  
    70  // IngressSpecToV1 converts a beta1 spec to the equivalent v1 version.
    71  func IngressSpecToV1(in *networkingv1beta1.IngressSpec) *networkingv1.IngressSpec {
    72  	if in == nil {
    73  		return nil
    74  	}
    75  	out := &networkingv1.IngressSpec{
    76  		IngressClassName: in.IngressClassName,
    77  		DefaultBackend:   backendToV1(in.Backend),
    78  	}
    79  	for _, tls := range in.TLS {
    80  		out.TLS = append(out.TLS, networkingv1.IngressTLS{
    81  			Hosts:      tls.Hosts,
    82  			SecretName: tls.SecretName,
    83  		})
    84  	}
    85  	for _, rule := range in.Rules {
    86  		out.Rules = append(out.Rules, networkingv1.IngressRule{
    87  			Host: rule.Host,
    88  			IngressRuleValue: networkingv1.IngressRuleValue{
    89  				HTTP: httpRuleToV1(rule.HTTP),
    90  			},
    91  		})
    92  	}
    93  	return out
    94  }
    95  
    96  func backendToV1(in *networkingv1beta1.IngressBackend) *networkingv1.IngressBackend {
    97  	if in == nil {
    98  		return nil
    99  	}
   100  	out := &networkingv1.IngressBackend{
   101  		Service:  serviceToV1(in),
   102  		Resource: in.Resource,
   103  	}
   104  	return out
   105  }
   106  
   107  func serviceToV1(in *networkingv1beta1.IngressBackend) *networkingv1.IngressServiceBackend {
   108  	if in == nil || in.ServiceName == "" {
   109  		return nil
   110  	}
   111  	out := &networkingv1.IngressServiceBackend{
   112  		Name: in.ServiceName,
   113  		Port: networkingv1.ServiceBackendPort{
   114  			Name:   in.ServicePort.StrVal,
   115  			Number: in.ServicePort.IntVal,
   116  		},
   117  	}
   118  	return out
   119  }
   120  
   121  func httpRuleToV1(in *networkingv1beta1.HTTPIngressRuleValue) *networkingv1.HTTPIngressRuleValue {
   122  	if in == nil {
   123  		return nil
   124  	}
   125  	out := &networkingv1.HTTPIngressRuleValue{}
   126  	for _, path := range in.Paths {
   127  		outPath := networkingv1.HTTPIngressPath{
   128  			Path:    path.Path,
   129  			Backend: *backendToV1(&path.Backend),
   130  		}
   131  		pathType := networkingv1.PathTypeImplementationSpecific
   132  		if path.PathType != nil {
   133  			pathType = networkingv1.PathType(*path.PathType)
   134  		}
   135  		outPath.PathType = &pathType
   136  		out.Paths = append(out.Paths, outPath)
   137  	}
   138  	return out
   139  }
   140  
   141  // IngressSpecFromV1 converts a v1 spec to the equivalent v1beta1 version.
   142  func IngressSpecFromV1(in *networkingv1.IngressSpec) *networkingv1beta1.IngressSpec {
   143  	if in == nil {
   144  		return nil
   145  	}
   146  	out := &networkingv1beta1.IngressSpec{
   147  		IngressClassName: in.IngressClassName,
   148  		Backend:          backendToBeta1(in.DefaultBackend),
   149  	}
   150  	for _, tls := range in.TLS {
   151  		out.TLS = append(out.TLS, networkingv1beta1.IngressTLS{
   152  			Hosts:      tls.Hosts,
   153  			SecretName: tls.SecretName,
   154  		})
   155  	}
   156  	for _, rule := range in.Rules {
   157  		out.Rules = append(out.Rules, networkingv1beta1.IngressRule{
   158  			Host: rule.Host,
   159  			IngressRuleValue: networkingv1beta1.IngressRuleValue{
   160  				HTTP: httpRuleToBeta1(rule.HTTP),
   161  			},
   162  		})
   163  	}
   164  	return out
   165  }
   166  
   167  func backendToBeta1(in *networkingv1.IngressBackend) *networkingv1beta1.IngressBackend {
   168  	if in == nil {
   169  		return nil
   170  	}
   171  	out := &networkingv1beta1.IngressBackend{
   172  		Resource: in.Resource,
   173  	}
   174  	if in.Service != nil {
   175  		out.ServiceName = in.Service.Name
   176  		if in.Service.Port.Name != "" {
   177  			out.ServicePort = intstr.FromString(in.Service.Port.Name)
   178  		} else {
   179  			out.ServicePort = intstr.FromInt(int(in.Service.Port.Number))
   180  		}
   181  	}
   182  	return out
   183  }
   184  
   185  func httpRuleToBeta1(in *networkingv1.HTTPIngressRuleValue) *networkingv1beta1.HTTPIngressRuleValue {
   186  	if in == nil {
   187  		return nil
   188  	}
   189  	out := &networkingv1beta1.HTTPIngressRuleValue{}
   190  	for _, path := range in.Paths {
   191  		outPath := networkingv1beta1.HTTPIngressPath{
   192  			Path:    path.Path,
   193  			Backend: *backendToBeta1(&path.Backend),
   194  		}
   195  		if path.PathType != nil {
   196  			pathType := networkingv1beta1.PathType(*path.PathType)
   197  			outPath.PathType = &pathType
   198  		}
   199  		out.Paths = append(out.Paths, outPath)
   200  	}
   201  	return out
   202  }