multus-cni/pkg/server/api/shim.go
Tomofumi Hayashi 7eb9673a1a Call GC command with valid attachments from multus cache
This code changes CNI's GC command argument. Previously it just
passes from parent CNI runtime, however, it may causes unexpected
resource deletion if one CNI plugin is used in both cluster
network and net-attach-def. This change generates valid attachments
from multus CNI cache and passed to delegate CNI plugin.
2024-12-20 11:28:41 +09:00

162 lines
4.9 KiB
Go

// Copyright (c) 2022 Multus 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.
package api
import (
"encoding/json"
"fmt"
"os"
"strings"
"github.com/containernetworking/cni/pkg/skel"
cnitypes "github.com/containernetworking/cni/pkg/types"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/logging"
)
// ShimNetConf for the SHIM cni config file written in json
type ShimNetConf struct {
// Note: This struct contains NetConf in pkg/types, but this struct is only used to parse
// following fields, so we skip to include NetConf here. Other fields are directly send to
// multus-daemon as a part of skel.CmdArgs, StdinData.
// types.NetConf
CNIVersion string `json:"cniVersion,omitempty"`
MultusSocketDir string `json:"daemonSocketDir"`
LogFile string `json:"logFile,omitempty"`
LogLevel string `json:"logLevel,omitempty"`
LogToStderr bool `json:"logToStderr,omitempty"`
}
// readyCheckFunc defines a type for API readiness check functions
type readyCheckFunc func(string) error
// CmdAdd implements the CNI spec ADD command handler
func CmdAdd(args *skel.CmdArgs) error {
response, cniVersion, err := postRequest(args, WaitUntilAPIReady)
if err != nil {
return logging.Errorf("CmdAdd (shim): %v", err)
}
logging.Verbosef("CmdAdd (shim): %v", *response.Result)
return cnitypes.PrintResult(response.Result, cniVersion)
}
// CmdCheck implements the CNI spec CHECK command handler
func CmdCheck(args *skel.CmdArgs) error {
_, _, err := postRequest(args, WaitUntilAPIReady)
if err != nil {
return logging.Errorf("CmdCheck (shim): %v", err)
}
return err
}
// CmdDel implements the CNI spec DEL command handler
func CmdDel(args *skel.CmdArgs) error {
_, _, err := postRequest(args, CheckAPIReadyNow)
if err != nil {
// No error in DEL (as of CNI spec)
logging.Errorf("CmdDel (shim): %v", err)
}
return nil
}
// CmdGC implements the CNI spec GC command handler
func CmdGC(args *skel.CmdArgs) error {
_, _, err := postRequest(args, WaitUntilAPIReady)
if err != nil {
return logging.Errorf("CmdGC (shim): %v", err)
}
return nil
}
// CmdStatus implements the CNI spec STATUS command handler
func CmdStatus(args *skel.CmdArgs) error {
_, _, err := postRequest(args, WaitUntilAPIReady)
if err != nil {
return logging.Errorf("CmdStatus (shim): %v", err)
}
return nil
}
func postRequest(args *skel.CmdArgs, readinessCheck readyCheckFunc) (*Response, string, error) {
multusShimConfig, err := shimConfig(args.StdinData)
if err != nil {
return nil, "", fmt.Errorf("invalid CNI configuration passed to multus-shim: %w", err)
}
// Execute the readiness check as necessary (e.g. don't wait on CNI DEL)
if err := readinessCheck(multusShimConfig.MultusSocketDir); err != nil {
return nil, multusShimConfig.CNIVersion, err
}
cniRequest, err := newCNIRequest(args)
if err != nil {
return nil, multusShimConfig.CNIVersion, err
}
var body []byte
body, err = DoCNI("http://dummy/cni", cniRequest, SocketPath(multusShimConfig.MultusSocketDir))
if err != nil {
return nil, multusShimConfig.CNIVersion, fmt.Errorf("%s: StdinData: %s", err.Error(), string(args.StdinData))
}
response := &Response{}
if len(body) != 0 {
if err = json.Unmarshal(body, response); err != nil {
err = fmt.Errorf("failed to unmarshal response '%s': %v", string(body), err)
return nil, multusShimConfig.CNIVersion, err
}
}
return response, multusShimConfig.CNIVersion, nil
}
// Create and fill a Request with this Plugin's environment and stdin which
// contain the CNI variables and configuration
func newCNIRequest(args *skel.CmdArgs) (*Request, error) {
envMap := make(map[string]string)
for _, item := range os.Environ() {
idx := strings.Index(item, "=")
if idx > 0 {
envMap[strings.TrimSpace(item[:idx])] = item[idx+1:]
}
}
return &Request{
Env: envMap,
Config: args.StdinData,
}, nil
}
func shimConfig(cniConfig []byte) (*ShimNetConf, error) {
multusConfig := &ShimNetConf{}
if err := json.Unmarshal(cniConfig, multusConfig); err != nil {
return nil, fmt.Errorf("failed to gather the multus configuration: %w", err)
}
if multusConfig.MultusSocketDir == "" {
multusConfig.MultusSocketDir = defaultMultusRunDir
}
// Logging
logging.SetLogStderr(multusConfig.LogToStderr)
if multusConfig.LogFile != "" {
logging.SetLogFile(multusConfig.LogFile)
}
if multusConfig.LogLevel != "" {
logging.SetLogLevel(multusConfig.LogLevel)
}
return multusConfig, nil
}