Skip to content

Commit

Permalink
resume and complete gc impl
Browse files Browse the repository at this point in the history
Signed-off-by: Henry Wang <henwang@amazon.com>
  • Loading branch information
henry118 committed Oct 13, 2023
1 parent 02155e0 commit e86c9ca
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 59 deletions.
31 changes: 13 additions & 18 deletions libcni/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ package libcni
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -114,7 +113,7 @@ type CNI interface {
ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error)

GCNetworkList(ctx context.Context, net *NetworkConfigList, args GCArgs) error
GCNetworkList(ctx context.Context, net *NetworkConfigList, args *GCArgs) error

GetCachedAttachments(containerID string) ([]*NetworkAttachment, error)
}
Expand Down Expand Up @@ -758,7 +757,7 @@ func (c *CNIConfig) GetVersionInfo(ctx context.Context, pluginType string) (vers
// GCNetworkList will do two things
// - dump the list of cached attachments, and issue deletes as necessary
// - issue a GC to the underlying plugins (if the version is high enough)
func (c *CNIConfig) GCNetworkList(ctx context.Context, list *NetworkConfigList, args GCArgs) error {
func (c *CNIConfig) GCNetworkList(ctx context.Context, list *NetworkConfigList, args *GCArgs) error {
// First, get the list of cached attachments
cachedAttachments, err := c.GetCachedAttachments("")
if err != nil {
Expand All @@ -770,28 +769,29 @@ func (c *CNIConfig) GCNetworkList(ctx context.Context, list *NetworkConfigList,
validAttachments[a] = nil
}

errs := []error{}
var errs []error

for _, cachedAttachment := range cachedAttachments {
if cachedAttachment.Network != list.Name {
continue
}
// we found this attachment
if _, ok := validAttachments[GCAttachment{ContainerID: cachedAttachment.ContainerID, IfName: cachedAttachment.IfName}]; ok {
gca := GCAttachment{
ContainerID: cachedAttachment.ContainerID,
IfName: cachedAttachment.IfName,
}
if _, ok := validAttachments[gca]; ok {
continue
}

// otherwise, this attachment wasn't valid
// and we should issue a CNI DEL
// otherwise, this attachment wasn't valid and we should issue a CNI DEL
rt := RuntimeConf{
ContainerID: cachedAttachment.ContainerID,
NetNS: cachedAttachment.NetNS,
IfName: cachedAttachment.IfName,
Args: cachedAttachment.CniArgs,
CapabilityArgs: cachedAttachment.CapabilityArgs,
}
err := c.DelNetworkList(ctx, list, &rt)
if err != nil {
if err := c.DelNetworkList(ctx, list, &rt); err != nil {
errs = append(errs, fmt.Errorf("failed to delete stale attachment %s %s: %w", rt.ContainerID, rt.IfName, err))
}
}
Expand All @@ -807,20 +807,15 @@ func (c *CNIConfig) GCNetworkList(ctx context.Context, list *NetworkConfigList,
// build config here
pluginConfig, err := InjectConf(plugin, inject)
if err != nil {
errs = append(errs, fmt.Errorf(
"failed to generate configuration to GC plugin %s: %w",
plugin.Network.Type, err))
errs = append(errs, fmt.Errorf("failed to generate configuration to GC plugin %s: %w", plugin.Network.Type, err))
}
if err := c.gcNetwork(ctx, pluginConfig); err != nil {
errs = append(errs, fmt.Errorf(
"failed to GC plugin %s: %w",
plugin.Network.Type, err))
errs = append(errs, fmt.Errorf("failed to GC plugin %s: %w", plugin.Network.Type, err))
}
}

}

return errors.Join(errs...)
return joinErrors(errs...)
}

func (c *CNIConfig) gcNetwork(ctx context.Context, net *NetworkConfig) error {
Expand Down
65 changes: 60 additions & 5 deletions libcni/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1513,16 +1513,71 @@ var _ = Describe("Invoking plugins", func() {
_, err := cniConfig.AddNetworkList(ctx, netConfigList, runtimeConfig)
Expect(err).NotTo(HaveOccurred())

By("Issuing a GC with valid networks")
gcargs := &libcni.GCArgs{
ValidAttachments: []libcni.GCAttachment{{
ContainerID: runtimeConfig.ContainerID,
IfName: runtimeConfig.IfName,
}},
}
err = cniConfig.GCNetworkList(ctx, netConfigList, gcargs)
Expect(err).NotTo(HaveOccurred())

By("Issuing a GC with no valid networks")
err = cniConfig.GCNetworkList(ctx, netConfigList, libcni.GCArgs{
ValidAttachments: []libcni.GCAttachment{},
})
gcargs.ValidAttachments = nil
err = cniConfig.GCNetworkList(ctx, netConfigList, gcargs)
Expect(err).NotTo(HaveOccurred())

commands, err := noop_debug.ReadCommandLog(plugins[0].commandFilePath)
Expect(err).NotTo(HaveOccurred())
Expect(commands).To(Equal([]string{"ADD", "DEL", "GC"}))

Expect(commands).To(HaveLen(4))

validations := []struct {
name string
fn func(entry noop_debug.CmdLogEntry)
}{
{
name: "ADD",
fn: func(entry noop_debug.CmdLogEntry) {
Expect(entry.CmdArgs.ContainerID).To(Equal(runtimeConfig.ContainerID))
Expect(entry.CmdArgs.IfName).To(Equal(runtimeConfig.IfName))
},
},
{
name: "GC",
fn: func(entry noop_debug.CmdLogEntry) {
var conf struct {
Attachments []map[string]string `json:"cni.dev/valid-attachments"`
}
err = json.Unmarshal(entry.CmdArgs.StdinData, &conf)
Expect(err).NotTo(HaveOccurred())
Expect(conf.Attachments).To(HaveLen(1))
Expect(conf.Attachments[0]).To(Equal(map[string]string{"containerID": runtimeConfig.ContainerID, "ifname": runtimeConfig.IfName}))
},
},
{
name: "DEL",
fn: func(entry noop_debug.CmdLogEntry) {
Expect(entry.CmdArgs.ContainerID).To(Equal(runtimeConfig.ContainerID))
Expect(entry.CmdArgs.IfName).To(Equal(runtimeConfig.IfName))
},
},
{
name: "GC",
fn: func(entry noop_debug.CmdLogEntry) {
var conf struct {
Attachments []map[string]string `json:"cni.dev/valid-attachments"`
}
err = json.Unmarshal(entry.CmdArgs.StdinData, &conf)
Expect(err).NotTo(HaveOccurred())
Expect(conf.Attachments).To(BeEmpty())
},
},
}
for i, c := range validations {
Expect(commands[i].Command).To(Equal(c.name))
c.fn(commands[i])
}
})
})
})
Expand Down
58 changes: 58 additions & 0 deletions libcni/multierror.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Copyright the CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Adapted from errors/join.go from go 1.20
// This package can be removed once the toolchain is updated to 1.20

package libcni

func joinErrors(errs ...error) error {
n := 0
for _, err := range errs {
if err != nil {
n++
}
}
if n == 0 {
return nil
}
e := &multiError{
errs: make([]error, 0, n),
}
for _, err := range errs {
if err != nil {
e.errs = append(e.errs, err)
}
}
return e
}

type multiError struct {
errs []error
}

func (e *multiError) Error() string {
var b []byte
for i, err := range e.errs {
if i > 0 {
b = append(b, '\n')
}
b = append(b, err.Error()...)
}
return string(b)
}
Loading

0 comments on commit e86c9ca

Please sign in to comment.