Skip to content

Commit 619091d

Browse files
authored
Merge pull request #579 from stuggi/nad_annotation_gw
Add EnsureNetworksAnnotation as an alternative for CreateNetworksAnnotation
2 parents 71a0e9d + 9e433ca commit 619091d

File tree

3 files changed

+195
-0
lines changed

3 files changed

+195
-0
lines changed

modules/common/networkattachment/networkattachment.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,17 @@ limitations under the License.
1717
package networkattachment
1818

1919
import (
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
5256
func 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+
}

modules/common/networkattachment/networkattachment_test.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"testing"
2121

2222
networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2324

2425
. "github.com/onsi/gomega"
2526
)
@@ -169,3 +170,124 @@ func TestGetNetworkIFName(t *testing.T) {
169170
})
170171
}
171172
}
173+
174+
func TestEnsureNetworksAnnotation(t *testing.T) {
175+
176+
tests := []struct {
177+
name string
178+
nadList []networkv1.NetworkAttachmentDefinition
179+
want map[string]string
180+
}{
181+
{
182+
name: "No network",
183+
nadList: []networkv1.NetworkAttachmentDefinition{},
184+
want: map[string]string{networkv1.NetworkAttachmentAnnot: "[]"},
185+
},
186+
{
187+
name: "Single network",
188+
nadList: []networkv1.NetworkAttachmentDefinition{
189+
{
190+
ObjectMeta: metav1.ObjectMeta{Name: "one", Namespace: "foo"},
191+
Spec: networkv1.NetworkAttachmentDefinitionSpec{
192+
Config: `
193+
{
194+
"cniVersion": "0.3.1",
195+
"name": "internalapi",
196+
"type": "macvlan",
197+
"master": "internalapi",
198+
"ipam": {
199+
"type": "whereabouts",
200+
"range": "172.17.0.0/24",
201+
"range_start": "172.17.0.30",
202+
"range_end": "172.17.0.70"
203+
}
204+
}
205+
`,
206+
},
207+
},
208+
},
209+
want: map[string]string{networkv1.NetworkAttachmentAnnot: "[{\"name\":\"one\",\"namespace\":\"foo\",\"interface\":\"one\"}]"},
210+
},
211+
{
212+
name: "Multiple networks",
213+
nadList: []networkv1.NetworkAttachmentDefinition{
214+
{
215+
ObjectMeta: metav1.ObjectMeta{Name: "one", Namespace: "foo"},
216+
Spec: networkv1.NetworkAttachmentDefinitionSpec{
217+
Config: `
218+
{
219+
"cniVersion": "0.3.1",
220+
"name": "internalapi",
221+
"type": "macvlan",
222+
"master": "internalapi",
223+
"ipam": {
224+
"type": "whereabouts",
225+
"range": "172.17.0.0/24",
226+
"range_start": "172.17.0.30",
227+
"range_end": "172.17.0.70"
228+
}
229+
}
230+
`,
231+
},
232+
},
233+
{
234+
ObjectMeta: metav1.ObjectMeta{Name: "two", Namespace: "foo"},
235+
Spec: networkv1.NetworkAttachmentDefinitionSpec{
236+
Config: `
237+
{
238+
"cniVersion": "0.3.1",
239+
"name": "tenant",
240+
"type": "macvlan",
241+
"master": "tenant",
242+
"ipam": {
243+
"type": "whereabouts",
244+
"range": "172.19.0.0/24",
245+
"range_start": "172.19.0.30",
246+
"range_end": "172.19.0.70"
247+
}
248+
}
249+
`,
250+
},
251+
},
252+
},
253+
want: map[string]string{networkv1.NetworkAttachmentAnnot: "[{\"name\":\"one\",\"namespace\":\"foo\",\"interface\":\"one\"},{\"name\":\"two\",\"namespace\":\"foo\",\"interface\":\"two\"}]"},
254+
},
255+
{
256+
name: "With gateway defined",
257+
nadList: []networkv1.NetworkAttachmentDefinition{
258+
{
259+
ObjectMeta: metav1.ObjectMeta{Name: "one", Namespace: "foo"},
260+
Spec: networkv1.NetworkAttachmentDefinitionSpec{
261+
Config: `
262+
{
263+
"cniVersion": "0.3.1",
264+
"name": "internalapi",
265+
"type": "macvlan",
266+
"master": "internalapi",
267+
"ipam": {
268+
"type": "whereabouts",
269+
"range": "172.17.0.0/24",
270+
"range_start": "172.17.0.30",
271+
"range_end": "172.17.0.70",
272+
"gateway": "172.17.0.1"
273+
}
274+
}
275+
`,
276+
},
277+
},
278+
},
279+
want: map[string]string{networkv1.NetworkAttachmentAnnot: "[{\"name\":\"one\",\"namespace\":\"foo\",\"interface\":\"one\",\"default-route\":[\"172.17.0.1\"]}]"},
280+
},
281+
}
282+
283+
for _, tt := range tests {
284+
t.Run(tt.name, func(t *testing.T) {
285+
g := NewWithT(t)
286+
287+
networkAnnotation, err := EnsureNetworksAnnotation(tt.nadList)
288+
g.Expect(err).NotTo(HaveOccurred())
289+
g.Expect(networkAnnotation).To(HaveLen(len(tt.want)))
290+
g.Expect(networkAnnotation).To(BeEquivalentTo(tt.want))
291+
})
292+
}
293+
}

modules/storage/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)