@@ -17,14 +17,17 @@ limitations under the License.
1717package networkattachment
1818
1919import (
20+ "bytes"
2021 "context"
2122 "encoding/json"
2223 "fmt"
24+ "net"
2325
2426 networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
2527 "github.com/openstack-k8s-operators/lib-common/modules/common/helper"
2628 "github.com/openstack-k8s-operators/lib-common/modules/common/pod"
2729 "k8s.io/apimachinery/pkg/types"
30+ "k8s.io/client-go/util/jsonpath"
2831)
2932
3033// GetNADWithName - Get network-attachment-definition with name in namespace
@@ -49,6 +52,7 @@ func GetNADWithName(
4952
5053// CreateNetworksAnnotation returns pod annotation for network-attachment-definition list
5154// e.g. k8s.v1.cni.cncf.io/networks: '[{"name": "internalapi", "namespace": "openstack"},{"name": "storage", "namespace": "openstack"}]'
55+ // NOTE: Deprecated, use EnsureNetworksAnnotation
5256func CreateNetworksAnnotation (namespace string , nads []string ) (map [string ]string , error ) {
5357
5458 netAnnotations := []networkv1.NetworkSelectionElement {}
@@ -136,3 +140,67 @@ func VerifyNetworkStatusFromAnnotation(
136140
137141 return networkReady , networkAttachmentStatus , nil
138142}
143+
144+ // EnsureNetworksAnnotation returns pod annotation for network-attachment-definition list
145+ // e.g. k8s.v1.cni.cncf.io/networks: '[{"name": "internalapi", "namespace": "openstack"},{"name": "storage", "namespace": "openstack"}]'
146+ // If `ipam.gateway` is defined in the NAD, the annotation will contain the `default-route` for that network:
147+ // e.g. k8s.v1.cni.cncf.io/networks: '[{"name":"internalapi","namespace":"openstack","interface":"internalapi","default-route":["10.1.2.200"]}]'
148+ func EnsureNetworksAnnotation (
149+ nadList []networkv1.NetworkAttachmentDefinition ,
150+ ) (map [string ]string , error ) {
151+
152+ annotationString := map [string ]string {}
153+ netAnnotations := []networkv1.NetworkSelectionElement {}
154+ for _ , nad := range nadList {
155+ gateway := ""
156+
157+ var data interface {}
158+ if err := json .Unmarshal ([]byte (nad .Spec .Config ), & data ); err != nil {
159+ return nil , fmt .Errorf ("failed to unmarshal JSON data: %w" , err )
160+ }
161+
162+ // use jsonpath to parse the cni config
163+ jp := jsonpath .New (nad .Name )
164+ jp .AllowMissingKeys (true ) // Allow missing keys, for when no gateway configured
165+
166+ // Parse the JSONPath template, for now just `ipam.gateway`
167+ err := jp .Parse (`{.ipam.gateway}` )
168+ if err != nil {
169+ return annotationString , fmt .Errorf ("parse template error: %w" , err )
170+ }
171+
172+ buf := new (bytes.Buffer )
173+ // get the gateway from the config
174+ err = jp .Execute (buf , data )
175+ if err != nil {
176+ return annotationString , fmt .Errorf ("parse execute template against nad %+v error: %w" , nad .Spec .Config , err )
177+ }
178+
179+ gateway = buf .String ()
180+
181+ gatewayReq := []net.IP {}
182+ if gateway != "" {
183+ gatewayReq = append (gatewayReq , net .ParseIP (gateway ))
184+
185+ }
186+
187+ netAnnotations = append (
188+ netAnnotations ,
189+ networkv1.NetworkSelectionElement {
190+ Name : nad .Name ,
191+ Namespace : nad .Namespace ,
192+ InterfaceRequest : GetNetworkIFName (nad .Name ),
193+ GatewayRequest : gatewayReq ,
194+ },
195+ )
196+ }
197+
198+ networks , err := json .Marshal (netAnnotations )
199+ if err != nil {
200+ return nil , fmt .Errorf ("failed to encode networks %v into json: %w" , nadList , err )
201+ }
202+
203+ annotationString [networkv1 .NetworkAttachmentAnnot ] = string (networks )
204+
205+ return annotationString , nil
206+ }
0 commit comments