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 }