mirror of
https://github.com/k8snetworkplumbingwg/multus-cni.git
synced 2025-05-12 18:36:32 +00:00
Change thin container base image to distroless
This commit changes thin container base image to distroless to simplify container image. It replace old shell script entrypoint to golang implementation and implement multus installer (i.e. copy).
This commit is contained in:
parent
c129e72779
commit
dcf92c8e94
cmd
deployments
e2e/templates
hack
images
DockerfileDockerfile.arm32Dockerfile.arm64Dockerfile.openshiftDockerfile.ppc64leDockerfile.s390xentrypoint.sh
pkg/cmdutils
56
cmd/install_multus/main.go
Normal file
56
cmd/install_multus/main.go
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2023 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.
|
||||
|
||||
// This is a install tool for multus plugins
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/cmdutils"
|
||||
)
|
||||
|
||||
func main() {
|
||||
typeFlag := pflag.StringP("type", "t", "", "specify installer type (thick/thin)")
|
||||
destDir := pflag.StringP("dest-dir", "d", "/host/opt/cni/bin", "destination directory")
|
||||
helpFlag := pflag.BoolP("help", "h", false, "show help message and quit")
|
||||
|
||||
pflag.Parse()
|
||||
if *helpFlag {
|
||||
pflag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
multusFileName := ""
|
||||
switch *typeFlag {
|
||||
case "thick":
|
||||
multusFileName = "multus-shim"
|
||||
case "thin":
|
||||
multusFileName = "multus"
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "--type is missing or --type has invalid value\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err := cmdutils.CopyFileAtomic(fmt.Sprintf("/usr/src/multus-cni/bin/%s", multusFileName), *destDir, fmt.Sprintf("%s.temp", multusFileName), multusFileName)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to copy file %s: %v\n", multusFileName, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("multus %s copy succeeded!\n", multusFileName)
|
||||
}
|
587
cmd/thin_entrypoint/main.go
Normal file
587
cmd/thin_entrypoint/main.go
Normal file
@ -0,0 +1,587 @@
|
||||
// Copyright (c) 2023 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.
|
||||
|
||||
// This is a entrypoint for thin (stand-alone) images.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
b64 "encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/cmdutils"
|
||||
)
|
||||
|
||||
// Options stores command line options
|
||||
type Options struct {
|
||||
CNIBinDir string
|
||||
CNIConfDir string
|
||||
CNIVersion string
|
||||
MultusConfFile string
|
||||
MultusBinFile string // may be hidden or remove?
|
||||
SkipMultusBinaryCopy bool
|
||||
MultusKubeConfigFileHost string
|
||||
MultusMasterCNIFileName string
|
||||
NamespaceIsolation bool
|
||||
GlobalNamespaces string
|
||||
MultusAutoconfigDir string
|
||||
MultusLogToStderr bool
|
||||
MultusLogLevel string
|
||||
MultusLogFile string
|
||||
OverrideNetworkName bool
|
||||
CleanupConfigOnExit bool
|
||||
RenameConfFile bool
|
||||
ReadinessIndicatorFile string
|
||||
AdditionalBinDir string
|
||||
ForceCNIVersion bool
|
||||
SkipTLSVerify bool
|
||||
}
|
||||
|
||||
const (
|
||||
serviceAccountTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
||||
serviceAccountCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
|
||||
)
|
||||
|
||||
func (o *Options) addFlags() {
|
||||
pflag.ErrHelp = nil // suppress error message for help
|
||||
fs := pflag.CommandLine
|
||||
fs.StringVar(&o.CNIBinDir, "cni-bin-dir", "/host/opt/cni/bin", "CNI binary directory")
|
||||
fs.StringVar(&o.CNIConfDir, "cni-conf-dir", "/host/etc/cni/net.d", "CNI config directory")
|
||||
fs.StringVar(&o.CNIVersion, "cni-version", "", "CNI version for multus CNI config (e.g. '0.3.1')")
|
||||
fs.StringVar(&o.MultusConfFile, "multus-conf-file", "auto", "multus CNI config file")
|
||||
fs.StringVar(&o.MultusBinFile, "multus-bin-file", "/usr/src/multus-cni/bin/multus", "multus binary file path")
|
||||
fs.BoolVar(&o.SkipMultusBinaryCopy, "skip-multus-binary-copy", false, "skip multus binary file copy")
|
||||
|
||||
// XXX Note: idea: "auto", create kubeconfig and update auto. otherwise just use given file path
|
||||
fs.StringVar(&o.MultusKubeConfigFileHost, "multus-kubeconfig-file-host", "/etc/cni/net.d/multus.d/multus.kubeconfig", "kubeconfig for multus (used only with --multus-conf-file=auto)")
|
||||
fs.StringVar(&o.MultusMasterCNIFileName, "multus-master-cni-file-name", "", "master CNI file in multus-autoconfig-dir")
|
||||
fs.BoolVar(&o.NamespaceIsolation, "namespace-isolation", false, "namespace isolation")
|
||||
fs.StringVar(&o.GlobalNamespaces, "global-namespaces", "", "global namespaces, comma separated (used only with --namespace-isolation=true)")
|
||||
fs.StringVar(&o.MultusAutoconfigDir, "multus-autoconfig-dir", "/host/etc/cni/net.d", "multus autoconfig dir (used only with --multus-conf-file=auto)")
|
||||
fs.BoolVar(&o.MultusLogToStderr, "multus-log-to-stderr", true, "log to stderr")
|
||||
fs.StringVar(&o.MultusLogLevel, "multus-log-level", "", "multus log level")
|
||||
fs.StringVar(&o.MultusLogFile, "multus-log-file", "", "multus log file")
|
||||
fs.BoolVar(&o.OverrideNetworkName, "override-network-name", false, "override network name from master cni file (used only with --multus-conf-file=auto)")
|
||||
fs.BoolVar(&o.CleanupConfigOnExit, "cleanup-config-on-exit", false, "cleanup config file on exit (used only with --multus-conf-file=auto)")
|
||||
fs.BoolVar(&o.RenameConfFile, "rename-conf-file", false, "rename master config file to invalidate (used only with --multus-conf-file=auto)")
|
||||
fs.StringVar(&o.ReadinessIndicatorFile, "readiness-indicator-file", "", "readiness indicator file (used only with --multus-conf-file=auto)")
|
||||
fs.StringVar(&o.AdditionalBinDir, "additional-bin-dir", "", "adds binDir option to configuration (used only with --multus-conf-file=auto)")
|
||||
fs.BoolVar(&o.SkipTLSVerify, "skip-tls-verify", false, "skip TLS verify")
|
||||
fs.BoolVar(&o.ForceCNIVersion, "force-cni-version", false, "force cni version to '--cni-version' (only for e2e-kind testing)")
|
||||
fs.MarkHidden("force-cni-version")
|
||||
fs.MarkHidden("skip-tls-verify")
|
||||
}
|
||||
|
||||
func (o *Options) verifyFileExists() error {
|
||||
// CNIConfDir
|
||||
if _, err := os.Stat(o.CNIConfDir); err != nil {
|
||||
return fmt.Errorf("cni-conf-dir is not found: %v", err)
|
||||
}
|
||||
|
||||
// CNIBinDir
|
||||
if _, err := os.Stat(o.CNIBinDir); err != nil {
|
||||
return fmt.Errorf("cni-bin-dir is not found: %v", err)
|
||||
}
|
||||
|
||||
// MultusBinFile
|
||||
if _, err := os.Stat(o.MultusBinFile); err != nil {
|
||||
return fmt.Errorf("multus-bin-file is not found: %v", err)
|
||||
}
|
||||
|
||||
if o.MultusConfFile != "auto" {
|
||||
// MultusConfFile
|
||||
if _, err := os.Stat(o.MultusConfFile); err != nil {
|
||||
return fmt.Errorf("multus-conf-file is not found: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const kubeConfigTemplate = `# Kubeconfig file for Multus CNI plugin.
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- name: local
|
||||
cluster:
|
||||
server: {{ .KubeConfigHost }}
|
||||
{{ .KubeServerTLS }}
|
||||
users:
|
||||
- name: multus
|
||||
user:
|
||||
token: "{{ .KubeServiceAccountToken }}"
|
||||
contexts:
|
||||
- name: multus-context
|
||||
context:
|
||||
cluster: local
|
||||
user: multus
|
||||
current-context: multus-context
|
||||
`
|
||||
|
||||
func (o *Options) createKubeConfig(currentFileHash []byte) ([]byte, error) {
|
||||
// check file exists
|
||||
if _, err := os.Stat(serviceAccountTokenFile); err != nil {
|
||||
return nil, fmt.Errorf("service account token is not found: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(serviceAccountCAFile); err != nil {
|
||||
return nil, fmt.Errorf("service account ca is not found: %v", err)
|
||||
}
|
||||
|
||||
// create multus.d directory
|
||||
if err := os.MkdirAll(fmt.Sprintf("%s/multus.d", o.CNIConfDir), 0755); err != nil {
|
||||
return nil, fmt.Errorf("cannot create multus.d directory: %v", err)
|
||||
}
|
||||
|
||||
// get Kubernetes service protocol/host/port
|
||||
kubeProtocol := os.Getenv("KUBERNETES_SERVICE_PROTOCOL")
|
||||
if kubeProtocol == "" {
|
||||
kubeProtocol = "https"
|
||||
}
|
||||
kubeHost := os.Getenv("KUBERNETES_SERVICE_HOST")
|
||||
kubePort := os.Getenv("KUBERNETES_SERVICE_PORT")
|
||||
|
||||
// check tlsConfig
|
||||
tlsConfig := ""
|
||||
if o.SkipTLSVerify {
|
||||
tlsConfig = "insecure-skip-tls-verify: true"
|
||||
} else {
|
||||
// create tlsConfig by service account CA file
|
||||
caFileByte, err := os.ReadFile(serviceAccountCAFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read service account ca file: %v", err)
|
||||
}
|
||||
caFileB64 := bytes.ReplaceAll([]byte(b64.StdEncoding.EncodeToString(caFileByte)), []byte("\n"), []byte(""))
|
||||
tlsConfig = fmt.Sprintf("certificate-authority-data: %s", string(caFileB64))
|
||||
}
|
||||
|
||||
saTokenByte, err := os.ReadFile(serviceAccountTokenFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read service account token file: %v", err)
|
||||
}
|
||||
|
||||
// create kubeconfig by template and replace it by atomic
|
||||
tempKubeConfigFile := fmt.Sprintf("%s/multus.d/multus.kubeconfig.new", o.CNIConfDir)
|
||||
multusKubeConfig := fmt.Sprintf("%s/multus.d/multus.kubeconfig", o.CNIConfDir)
|
||||
fp, err := os.OpenFile(tempKubeConfigFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create kubeconfig temp file: %v", err)
|
||||
}
|
||||
|
||||
templateKubeconfig, err := template.New("kubeconfig").Parse(kubeConfigTemplate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("template parse error: %v", err)
|
||||
}
|
||||
templateData := map[string]string{
|
||||
"KubeConfigHost": fmt.Sprintf("%s://[%s]:%s", kubeProtocol, kubeHost, kubePort),
|
||||
"KubeServerTLS": tlsConfig,
|
||||
"KubeServiceAccountToken": string(saTokenByte),
|
||||
}
|
||||
|
||||
// Prepare
|
||||
hash := sha256.New()
|
||||
writer := io.MultiWriter(hash, fp)
|
||||
|
||||
// genearate kubeconfig from template
|
||||
if err = templateKubeconfig.Execute(writer, templateData); err != nil {
|
||||
return nil, fmt.Errorf("cannot create kubeconfig: %v", err)
|
||||
}
|
||||
|
||||
if err := fp.Sync(); err != nil {
|
||||
os.Remove(fp.Name())
|
||||
return nil, fmt.Errorf("cannot flush kubeconfig temp file: %v", err)
|
||||
}
|
||||
if err := fp.Close(); err != nil {
|
||||
os.Remove(fp.Name())
|
||||
return nil, fmt.Errorf("cannot close kubeconfig temp file: %v", err)
|
||||
}
|
||||
|
||||
newFileHash := hash.Sum(nil)
|
||||
if currentFileHash != nil && bytes.Compare(newFileHash, currentFileHash) == 0 {
|
||||
fmt.Printf("kubeconfig is same, not copy\n")
|
||||
os.Remove(fp.Name())
|
||||
return currentFileHash, nil
|
||||
}
|
||||
|
||||
// replace file with tempfile
|
||||
if err := os.Rename(tempKubeConfigFile, multusKubeConfig); err != nil {
|
||||
return nil, fmt.Errorf("cannot replace %q with temp file %q: %v", multusKubeConfig, tempKubeConfigFile, err)
|
||||
}
|
||||
|
||||
fmt.Printf("kubeconfig is created in %s\n", multusKubeConfig)
|
||||
return newFileHash, nil
|
||||
}
|
||||
|
||||
const multusConflistTemplate = `{
|
||||
"cniVersion": "{{ .CNIVersion }}",
|
||||
"name": "{{ .MasterPluginNetworkName }}",
|
||||
"plugins": [ {
|
||||
"type": "multus",{{
|
||||
.NestedCapabilities
|
||||
}}{{
|
||||
.NamespaceIsolationConfig
|
||||
}}{{
|
||||
.GlobalNamespacesConfig
|
||||
}}{{
|
||||
.LogToStderrConfig
|
||||
}}{{
|
||||
.LogLevelConfig
|
||||
}}{{
|
||||
.LogFileConfig
|
||||
}}{{
|
||||
.AdditionalBinDirConfig
|
||||
}}{{
|
||||
.ReadinessIndicatorFileConfig
|
||||
}}
|
||||
"kubeconfig": "{{ .MultusKubeConfigFileHost }}",
|
||||
"delegates": [
|
||||
{{ .MasterPluginJSON }}
|
||||
]
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const multusConfTemplate = `{
|
||||
"cniVersion": "{{ .CNIVersion }}",
|
||||
"name": "{{ .MasterPluginNetworkName }}",
|
||||
"type": "multus",{{
|
||||
.NestedCapabilities
|
||||
}}{{
|
||||
.NamespaceIsolationConfig
|
||||
}}{{
|
||||
.GlobalNamespacesConfig
|
||||
}}{{
|
||||
.LogToStderrConfig
|
||||
}}{{
|
||||
.LogLevelConfig
|
||||
}}{{
|
||||
.LogFileConfig
|
||||
}}{{
|
||||
.AdditionalBinDirConfig
|
||||
}}{{
|
||||
.ReadinessIndicatorFileConfig
|
||||
}}
|
||||
"kubeconfig": "{{ .MultusKubeConfigFileHost }}",
|
||||
"delegates": [
|
||||
{{ .MasterPluginJSON }}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
func (o *Options) createMultusConfig() (string, error) {
|
||||
// find master file from MultusAutoconfigDir
|
||||
files, err := libcni.ConfFiles(o.MultusAutoconfigDir, []string{".conf", ".conflist"})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot find master CNI config in %q: %v", o.MultusAutoconfigDir, err)
|
||||
}
|
||||
|
||||
masterConfigPath := files[0]
|
||||
masterConfigBytes, err := os.ReadFile(masterConfigPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot read master CNI config file %q: %v", masterConfigPath, err)
|
||||
}
|
||||
masterConfig := map[string]interface{}{}
|
||||
if err = json.Unmarshal(masterConfigBytes, &masterConfig); err != nil {
|
||||
return "", fmt.Errorf("cannot read master CNI config json: %v", err)
|
||||
}
|
||||
|
||||
// check CNIVersion
|
||||
masterCNIVersionElem, ok := masterConfig["cniVersion"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("cannot get cniVersion in master CNI config file %q: %v", masterConfigPath, err)
|
||||
}
|
||||
|
||||
if o.ForceCNIVersion {
|
||||
masterConfig["cniVersion"] = o.CNIVersion
|
||||
fmt.Printf("force CNI version to %q\n", o.CNIVersion)
|
||||
} else {
|
||||
masterCNIVersion := masterCNIVersionElem.(string)
|
||||
if o.CNIVersion != "" && masterCNIVersion != o.CNIVersion {
|
||||
return "", fmt.Errorf("Multus cni version is %q while master plugin cni version is %q", o.CNIVersion, masterCNIVersion)
|
||||
}
|
||||
o.CNIVersion = masterCNIVersion
|
||||
}
|
||||
cniVersionConfig := o.CNIVersion
|
||||
|
||||
// check OverrideNetworkName (if true, get master plugin name, otherwise 'multus-cni-network'
|
||||
masterPluginNetworkName := "multus-cni-network"
|
||||
if o.OverrideNetworkName {
|
||||
masterPluginNetworkElem, ok := masterConfig["name"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("cannot get name in master CNI config file %q: %v", masterConfigPath, err)
|
||||
}
|
||||
|
||||
masterPluginNetworkName = masterPluginNetworkElem.(string)
|
||||
fmt.Printf("master plugin name is overrided to %q\n", masterPluginNetworkName)
|
||||
}
|
||||
|
||||
// check capabilities (from master conf, top and 'plugins')
|
||||
masterCapabilities := map[string]bool{}
|
||||
_, isMasterConfList := masterConfig["plugins"]
|
||||
|
||||
if isMasterConfList {
|
||||
masterPluginsElem, ok := masterConfig["plugins"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("cannot get 'plugins' field in master CNI config file %q: %v", masterConfigPath, err)
|
||||
}
|
||||
masterPlugins := masterPluginsElem.([]interface{})
|
||||
for _, v := range masterPlugins {
|
||||
pluginFields := v.(map[string]interface{})
|
||||
capabilitiesElem, ok := pluginFields["capabilities"]
|
||||
if ok {
|
||||
capabilities := capabilitiesElem.(map[string]interface{})
|
||||
for k, v := range capabilities {
|
||||
masterCapabilities[k] = v.(bool)
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Printf("master capabilities is get from conflist\n")
|
||||
} else {
|
||||
masterCapabilitiesElem, ok := masterConfig["capabilities"]
|
||||
if ok {
|
||||
for k, v := range masterCapabilitiesElem.(map[string]interface{}) {
|
||||
masterCapabilities[k] = v.(bool)
|
||||
}
|
||||
}
|
||||
fmt.Printf("master capabilities is get from conffile\n")
|
||||
}
|
||||
nestedCapabilitiesConf := ""
|
||||
if len(masterCapabilities) != 0 {
|
||||
capabilitiesByte, err := json.Marshal(masterCapabilities)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot get capabilities map: %v", err)
|
||||
}
|
||||
nestedCapabilitiesConf = fmt.Sprintf("\n \"capabilities\": %s,", string(capabilitiesByte))
|
||||
}
|
||||
|
||||
// check NamespaceIsolation
|
||||
namespaceIsolationConfig := ""
|
||||
if o.NamespaceIsolation {
|
||||
namespaceIsolationConfig = "\n \"namespaceIsolation\": true,"
|
||||
}
|
||||
|
||||
// check GlobalNamespaces
|
||||
globalNamespaceConfig := ""
|
||||
if o.GlobalNamespaces != "" {
|
||||
globalNamespaceConfig = fmt.Sprintf("\n \"globalNamespaces\": %q,", o.GlobalNamespaces)
|
||||
}
|
||||
|
||||
// check MultusLogToStderr
|
||||
logToStderrConfig := ""
|
||||
if !o.MultusLogToStderr {
|
||||
logToStderrConfig = "\n \"logToStderr\": false,"
|
||||
}
|
||||
|
||||
// check MultusLogLevel (debug/error/panic/verbose) and reject others
|
||||
logLevelConfig := ""
|
||||
logLevelStr := strings.ToLower(o.MultusLogLevel)
|
||||
switch logLevelStr {
|
||||
case "debug", "error", "panic", "verbose":
|
||||
logLevelConfig = fmt.Sprintf("\n \"logLevel\": %q,", logLevelStr)
|
||||
case "":
|
||||
// no logLevel config, skipped
|
||||
default:
|
||||
return "", fmt.Errorf("Log levels should be one of: debug/verbose/error/panic, did not understand: %q", o.MultusLogLevel)
|
||||
}
|
||||
|
||||
// check MultusLogFile
|
||||
logFileConfig := ""
|
||||
if o.MultusLogFile != "" {
|
||||
logFileConfig = fmt.Sprintf("\n \"logFile\": %q,", o.MultusLogFile)
|
||||
}
|
||||
|
||||
// check AdditionalBinDir
|
||||
additionalBinDirConfig := ""
|
||||
if o.AdditionalBinDir != "" {
|
||||
additionalBinDirConfig = fmt.Sprintf("\n \"binDir\": %q,", o.AdditionalBinDir)
|
||||
}
|
||||
|
||||
// check ReadinessIndicatorFile
|
||||
readinessIndicatorFileConfig := ""
|
||||
if o.ReadinessIndicatorFile != "" {
|
||||
readinessIndicatorFileConfig = fmt.Sprintf("\n \"readinessindicatorfile\": %q,", o.ReadinessIndicatorFile)
|
||||
}
|
||||
|
||||
// fill .MasterPluginJSON
|
||||
masterPluginByte, err := json.Marshal(masterConfig)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot encode master CNI config: %v", err)
|
||||
}
|
||||
|
||||
// generate multus config
|
||||
tempFileName := fmt.Sprintf("%s/00-multus.conf.new", o.CNIConfDir)
|
||||
fp, err := os.OpenFile(tempFileName, os.O_WRONLY|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot create multus cni temp file: %v", err)
|
||||
}
|
||||
|
||||
// use conflist template if cniVersionConfig == "1.0.0"
|
||||
multusConfFilePath := fmt.Sprintf("%s/00-multus.conf", o.CNIConfDir)
|
||||
templateMultusConfig, err := template.New("multusCNIConfig").Parse(multusConfTemplate)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("template parse error: %v", err)
|
||||
}
|
||||
|
||||
if o.CNIVersion == "1.0.0" { //Check 1.0.0 or above!
|
||||
multusConfFilePath = fmt.Sprintf("%s/00-multus.conflist", o.CNIConfDir)
|
||||
templateMultusConfig, err = template.New("multusCNIConfig").Parse(multusConflistTemplate)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("template parse error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
templateData := map[string]string{
|
||||
"CNIVersion": cniVersionConfig,
|
||||
"MasterPluginNetworkName": masterPluginNetworkName,
|
||||
"NestedCapabilities": nestedCapabilitiesConf,
|
||||
"NamespaceIsolationConfig": namespaceIsolationConfig,
|
||||
"GlobalNamespacesConfig": globalNamespaceConfig,
|
||||
"LogToStderrConfig": logToStderrConfig,
|
||||
"LogLevelConfig": logLevelConfig,
|
||||
"LogFileConfig": logFileConfig,
|
||||
"AdditionalBinDirConfig": additionalBinDirConfig,
|
||||
"ReadinessIndicatorFileConfig": readinessIndicatorFileConfig,
|
||||
"MultusKubeConfigFileHost": o.MultusKubeConfigFileHost, // be fixed?
|
||||
"MasterPluginJSON": string(masterPluginByte),
|
||||
}
|
||||
if err = templateMultusConfig.Execute(fp, templateData); err != nil {
|
||||
return "", fmt.Errorf("cannot create multus cni config: %v", err)
|
||||
}
|
||||
|
||||
if err := fp.Sync(); err != nil {
|
||||
os.Remove(tempFileName)
|
||||
return "", fmt.Errorf("cannot flush multus cni config: %v", err)
|
||||
}
|
||||
if err := fp.Close(); err != nil {
|
||||
os.Remove(tempFileName)
|
||||
return "", fmt.Errorf("cannot close multus cni config: %v", err)
|
||||
}
|
||||
|
||||
if err := os.Rename(tempFileName, multusConfFilePath); err != nil {
|
||||
return "", fmt.Errorf("cannot replace %q with temp file %q: %v", multusConfFilePath, tempFileName, err)
|
||||
}
|
||||
|
||||
if o.RenameConfFile {
|
||||
//masterConfigPath
|
||||
renamedMasterConfigPath := fmt.Sprintf("%s.old", masterConfigPath)
|
||||
if err := os.Rename(masterConfigPath, renamedMasterConfigPath); err != nil {
|
||||
return "", fmt.Errorf("cannot move original master file to %q", renamedMasterConfigPath)
|
||||
}
|
||||
fmt.Printf("Original master file moved to %q\n", renamedMasterConfigPath)
|
||||
}
|
||||
|
||||
return masterConfigPath, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
opt := Options{}
|
||||
opt.addFlags()
|
||||
helpFlag := pflag.BoolP("help", "h", false, "show help message and quit")
|
||||
|
||||
pflag.Parse()
|
||||
if *helpFlag {
|
||||
pflag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err := opt.verifyFileExists()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
return
|
||||
}
|
||||
//XXX: RenameConfFile and CleanupConfigOnExit might be conflict, so need to check
|
||||
|
||||
// copy multus binary
|
||||
if !opt.SkipMultusBinaryCopy {
|
||||
// Copy
|
||||
if err = cmdutils.CopyFileAtomic(opt.MultusBinFile, opt.CNIBinDir, "_multus", "multus"); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed at multus copy: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var kubeConfigHash []byte
|
||||
var masterConfigFilePath string
|
||||
// copy user specified multus conf to CNI conf directory
|
||||
if opt.MultusConfFile != "auto" {
|
||||
confFileName := filepath.Base(opt.MultusConfFile)
|
||||
tempConfFileName := fmt.Sprintf("%s.temp", confFileName)
|
||||
if err = cmdutils.CopyFileAtomic(opt.MultusConfFile, opt.CNIConfDir, tempConfFileName, confFileName); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed at copy multus conf file: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("multus config file %s is copied.\n", opt.MultusConfFile)
|
||||
} else { // auto generate multus config
|
||||
kubeConfigHash, err = opt.createKubeConfig(nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to create multus kubeconfig: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("kubeconfig file is created.\n")
|
||||
masterConfigFilePath, err = opt.createMultusConfig()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to create multus config: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("multus config file is created.\n")
|
||||
}
|
||||
|
||||
if opt.CleanupConfigOnExit && opt.MultusConfFile == "auto" {
|
||||
fmt.Printf("Entering watch loop...\n")
|
||||
for {
|
||||
// Check kubeconfig and update if different (i.e. service account updated)
|
||||
kubeConfigHash, err = opt.createKubeConfig(kubeConfigHash)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to update multus kubeconfig: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: should we watch master CNI config (by fsnotify? https://github.com/fsnotify/fsnotify)
|
||||
_, err = os.Stat(masterConfigFilePath)
|
||||
|
||||
// if masterConfigFilePath is no longer exists
|
||||
if os.IsNotExist(err) {
|
||||
fmt.Printf("Master plugin @ %q has been deleted. Allowing 45 seconds for its restoration...\n", masterConfigFilePath)
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
for range time.Tick(1 * time.Second) {
|
||||
_, err = os.Stat(masterConfigFilePath)
|
||||
if !os.IsNotExist(err) {
|
||||
fmt.Printf("Master plugin @ %q was restored. Regenerating given configuration.\n", masterConfigFilePath)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
masterConfigFilePath, err = opt.createMultusConfig()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to create multus config: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// sleep infinitely
|
||||
for {
|
||||
time.Sleep(time.Duration(1<<63 - 1))
|
||||
}
|
||||
}
|
||||
}
|
402
cmd/thin_entrypoint/main_test.go
Normal file
402
cmd/thin_entrypoint/main_test.go
Normal file
@ -0,0 +1,402 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestThinEntrypoint(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "thin_entrypoint")
|
||||
}
|
||||
|
||||
var _ = Describe("thin entrypoint testing", func() {
|
||||
It("always pass just example", func() {
|
||||
a := 10
|
||||
Expect(a).To(Equal(10))
|
||||
})
|
||||
|
||||
It("Run verifyFileExists() with expected environment, autoconfig", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf_dir", tmpDir)
|
||||
cniBinDir := fmt.Sprintf("%s/cni_bin_dir", tmpDir)
|
||||
multusBinFile := fmt.Sprintf("%s/multus_bin", tmpDir)
|
||||
multusConfFile := fmt.Sprintf("%s/multus_conf", tmpDir)
|
||||
|
||||
// CNIConfDir
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// CNIBinDir
|
||||
Expect(os.Mkdir(cniBinDir, 0755)).To(Succeed())
|
||||
|
||||
// MultusBinFile
|
||||
Expect(os.WriteFile(multusBinFile, nil, 0744)).To(Succeed())
|
||||
|
||||
// MultusConfFile
|
||||
Expect(os.WriteFile(multusConfFile, nil, 0744)).To(Succeed())
|
||||
|
||||
err = (&Options{
|
||||
CNIConfDir: cniConfDir,
|
||||
CNIBinDir: cniBinDir,
|
||||
MultusBinFile: multusBinFile,
|
||||
MultusConfFile: multusConfFile,
|
||||
}).verifyFileExists()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run verifyFileExists() with invalid environmentMultusConfFile", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf_dir", tmpDir)
|
||||
cniBinDir := fmt.Sprintf("%s/cni_bin_dir", tmpDir)
|
||||
multusBinFile := fmt.Sprintf("%s/multus_bin", tmpDir)
|
||||
multusConfFile := fmt.Sprintf("%s/multus_conf", tmpDir)
|
||||
|
||||
// CNIConfDir
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// CNIBinDir
|
||||
Expect(os.Mkdir(cniBinDir, 0755)).To(Succeed())
|
||||
|
||||
// MultusConfFile
|
||||
Expect(os.WriteFile(multusConfFile, nil, 0744)).To(Succeed())
|
||||
|
||||
err = (&Options{
|
||||
CNIConfDir: cniConfDir,
|
||||
CNIBinDir: cniBinDir,
|
||||
MultusBinFile: multusBinFile,
|
||||
MultusConfFile: multusConfFile,
|
||||
}).verifyFileExists()
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run createMultusConfig(), default, conf", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
multusAutoConfigDir := fmt.Sprintf("%s/auto_conf", tmpDir)
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf", tmpDir)
|
||||
|
||||
Expect(os.Mkdir(multusAutoConfigDir, 0755)).To(Succeed())
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// create master CNI config
|
||||
masterCNIConfig := `
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "test1",
|
||||
"type": "cnitesttype"
|
||||
}`
|
||||
Expect(os.WriteFile(fmt.Sprintf("%s/10-testcni.conf", multusAutoConfigDir), []byte(masterCNIConfig), 0755)).To(Succeed())
|
||||
|
||||
masterConfigPath, err := (&Options{
|
||||
MultusAutoconfigDir: multusAutoConfigDir,
|
||||
CNIConfDir: cniConfDir,
|
||||
MultusKubeConfigFileHost: "/etc/foobar_kubeconfig",
|
||||
}).createMultusConfig()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(masterConfigPath).NotTo(Equal(""))
|
||||
|
||||
expectedResult := `{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "multus-cni-network",
|
||||
"type": "multus",
|
||||
"logToStderr": false,
|
||||
"kubeconfig": "/etc/foobar_kubeconfig",
|
||||
"delegates": [
|
||||
{"cniVersion":"0.3.1","name":"test1","type":"cnitesttype"}
|
||||
]
|
||||
}
|
||||
`
|
||||
conf, err := os.ReadFile(fmt.Sprintf("%s/00-multus.conf", cniConfDir))
|
||||
Expect(string(conf)).To(Equal(expectedResult))
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run createMultusConfig(), capabilities, conf", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
multusAutoConfigDir := fmt.Sprintf("%s/auto_conf", tmpDir)
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf", tmpDir)
|
||||
|
||||
Expect(os.Mkdir(multusAutoConfigDir, 0755)).To(Succeed())
|
||||
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// create master CNI config
|
||||
masterCNIConfig := `
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "test1",
|
||||
"capabilities": { "bandwidth": true },
|
||||
"type": "cnitesttype"
|
||||
}`
|
||||
Expect(os.WriteFile(fmt.Sprintf("%s/10-testcni.conf", multusAutoConfigDir), []byte(masterCNIConfig), 0755)).To(Succeed())
|
||||
|
||||
masterConfigPath, err := (&Options{
|
||||
MultusAutoconfigDir: multusAutoConfigDir,
|
||||
CNIConfDir: cniConfDir,
|
||||
MultusKubeConfigFileHost: "/etc/foobar_kubeconfig",
|
||||
}).createMultusConfig()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(masterConfigPath).NotTo(Equal(""))
|
||||
|
||||
expectedResult := `{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "multus-cni-network",
|
||||
"type": "multus",
|
||||
"capabilities": {"bandwidth":true},
|
||||
"logToStderr": false,
|
||||
"kubeconfig": "/etc/foobar_kubeconfig",
|
||||
"delegates": [
|
||||
{"capabilities":{"bandwidth":true},"cniVersion":"0.3.1","name":"test1","type":"cnitesttype"}
|
||||
]
|
||||
}
|
||||
`
|
||||
conf, err := os.ReadFile(fmt.Sprintf("%s/00-multus.conf", cniConfDir))
|
||||
Expect(string(conf)).To(Equal(expectedResult))
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run createMultusConfig(), with options, conf", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
multusAutoConfigDir := fmt.Sprintf("%s/auto_conf", tmpDir)
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf", tmpDir)
|
||||
|
||||
Expect(os.Mkdir(multusAutoConfigDir, 0755)).To(Succeed())
|
||||
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// create master CNI config
|
||||
masterCNIConfig := `
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "test1",
|
||||
"type": "cnitesttype"
|
||||
}`
|
||||
err = os.WriteFile(fmt.Sprintf("%s/10-testcni.conf", multusAutoConfigDir), []byte(masterCNIConfig), 0755)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
masterConfigPath, err := (&Options{
|
||||
MultusAutoconfigDir: multusAutoConfigDir,
|
||||
CNIConfDir: cniConfDir,
|
||||
MultusKubeConfigFileHost: "/etc/foobar_kubeconfig",
|
||||
NamespaceIsolation: true,
|
||||
GlobalNamespaces: "foobar,barfoo",
|
||||
MultusLogToStderr: true,
|
||||
MultusLogLevel: "DEBUG",
|
||||
MultusLogFile: "/tmp/foobar.log",
|
||||
AdditionalBinDir: "/tmp/add_bin_dir",
|
||||
ReadinessIndicatorFile: "/var/lib/foobar_indicator",
|
||||
}).createMultusConfig()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(masterConfigPath).NotTo(Equal(""))
|
||||
|
||||
expectedResult := `{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "multus-cni-network",
|
||||
"type": "multus",
|
||||
"namespaceIsolation": true,
|
||||
"globalNamespaces": "foobar,barfoo",
|
||||
"logLevel": "debug",
|
||||
"logFile": "/tmp/foobar.log",
|
||||
"binDir": "/tmp/add_bin_dir",
|
||||
"readinessindicatorfile": "/var/lib/foobar_indicator",
|
||||
"kubeconfig": "/etc/foobar_kubeconfig",
|
||||
"delegates": [
|
||||
{"cniVersion":"0.3.1","name":"test1","type":"cnitesttype"}
|
||||
]
|
||||
}
|
||||
`
|
||||
conf, err := os.ReadFile(fmt.Sprintf("%s/00-multus.conf", cniConfDir))
|
||||
Expect(string(conf)).To(Equal(expectedResult))
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run createMultusConfig(), default, conflist", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
multusAutoConfigDir := fmt.Sprintf("%s/auto_conf", tmpDir)
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf", tmpDir)
|
||||
|
||||
Expect(os.Mkdir(multusAutoConfigDir, 0755)).To(Succeed())
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// create master CNI config
|
||||
masterCNIConfig := `
|
||||
{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "test1",
|
||||
"type": "cnitesttype"
|
||||
}`
|
||||
Expect(os.WriteFile(fmt.Sprintf("%s/10-testcni.conf", multusAutoConfigDir), []byte(masterCNIConfig), 0755)).To(Succeed())
|
||||
|
||||
masterConfigPath, err := (&Options{
|
||||
MultusAutoconfigDir: multusAutoConfigDir,
|
||||
CNIConfDir: cniConfDir,
|
||||
MultusKubeConfigFileHost: "/etc/foobar_kubeconfig",
|
||||
}).createMultusConfig()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(masterConfigPath).NotTo(Equal(""))
|
||||
|
||||
expectedResult :=
|
||||
`{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "multus-cni-network",
|
||||
"plugins": [ {
|
||||
"type": "multus",
|
||||
"logToStderr": false,
|
||||
"kubeconfig": "/etc/foobar_kubeconfig",
|
||||
"delegates": [
|
||||
{"cniVersion":"1.0.0","name":"test1","type":"cnitesttype"}
|
||||
]
|
||||
}]
|
||||
}
|
||||
`
|
||||
conf, err := os.ReadFile(fmt.Sprintf("%s/00-multus.conflist", cniConfDir))
|
||||
Expect(string(conf)).To(Equal(expectedResult))
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run createMultusConfig(), capabilities, conflist", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
multusAutoConfigDir := fmt.Sprintf("%s/auto_conf", tmpDir)
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf", tmpDir)
|
||||
|
||||
Expect(os.Mkdir(multusAutoConfigDir, 0755)).To(Succeed())
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// create master CNI config
|
||||
masterCNIConfig := `
|
||||
{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "test1",
|
||||
"capabilities": { "bandwidth": true },
|
||||
"type": "cnitesttype"
|
||||
}`
|
||||
Expect(os.WriteFile(fmt.Sprintf("%s/10-testcni.conflist", multusAutoConfigDir), []byte(masterCNIConfig), 0755)).To(Succeed())
|
||||
|
||||
masterConfigPath, err := (&Options{
|
||||
MultusAutoconfigDir: multusAutoConfigDir,
|
||||
CNIConfDir: cniConfDir,
|
||||
MultusKubeConfigFileHost: "/etc/foobar_kubeconfig",
|
||||
}).createMultusConfig()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(masterConfigPath).NotTo(Equal(""))
|
||||
|
||||
expectedResult :=
|
||||
`{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "multus-cni-network",
|
||||
"plugins": [ {
|
||||
"type": "multus",
|
||||
"capabilities": {"bandwidth":true},
|
||||
"logToStderr": false,
|
||||
"kubeconfig": "/etc/foobar_kubeconfig",
|
||||
"delegates": [
|
||||
{"capabilities":{"bandwidth":true},"cniVersion":"1.0.0","name":"test1","type":"cnitesttype"}
|
||||
]
|
||||
}]
|
||||
}
|
||||
`
|
||||
conf, err := os.ReadFile(fmt.Sprintf("%s/00-multus.conflist", cniConfDir))
|
||||
Expect(string(conf)).To(Equal(expectedResult))
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run createMultusConfig(), with options, conflist", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
multusAutoConfigDir := fmt.Sprintf("%s/auto_conf", tmpDir)
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf", tmpDir)
|
||||
|
||||
Expect(os.Mkdir(multusAutoConfigDir, 0755)).To(Succeed())
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// create master CNI config
|
||||
masterCNIConfig := `
|
||||
{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "test1",
|
||||
"type": "cnitesttype"
|
||||
}`
|
||||
Expect(os.WriteFile(fmt.Sprintf("%s/10-testcni.conflist", multusAutoConfigDir), []byte(masterCNIConfig), 0755)).To(Succeed())
|
||||
|
||||
masterConfigPath, err := (&Options{
|
||||
MultusAutoconfigDir: multusAutoConfigDir,
|
||||
CNIConfDir: cniConfDir,
|
||||
MultusKubeConfigFileHost: "/etc/foobar_kubeconfig",
|
||||
NamespaceIsolation: true,
|
||||
GlobalNamespaces: "foobar,barfoo",
|
||||
MultusLogToStderr: true,
|
||||
MultusLogLevel: "DEBUG",
|
||||
MultusLogFile: "/tmp/foobar.log",
|
||||
AdditionalBinDir: "/tmp/add_bin_dir",
|
||||
ReadinessIndicatorFile: "/var/lib/foobar_indicator",
|
||||
}).createMultusConfig()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(masterConfigPath).NotTo(Equal(""))
|
||||
|
||||
expectedResult :=
|
||||
`{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "multus-cni-network",
|
||||
"plugins": [ {
|
||||
"type": "multus",
|
||||
"namespaceIsolation": true,
|
||||
"globalNamespaces": "foobar,barfoo",
|
||||
"logLevel": "debug",
|
||||
"logFile": "/tmp/foobar.log",
|
||||
"binDir": "/tmp/add_bin_dir",
|
||||
"readinessindicatorfile": "/var/lib/foobar_indicator",
|
||||
"kubeconfig": "/etc/foobar_kubeconfig",
|
||||
"delegates": [
|
||||
{"cniVersion":"1.0.0","name":"test1","type":"cnitesttype"}
|
||||
]
|
||||
}]
|
||||
}
|
||||
`
|
||||
conf, err := os.ReadFile(fmt.Sprintf("%s/00-multus.conflist", cniConfDir))
|
||||
Expect(string(conf)).To(Equal(expectedResult))
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
})
|
@ -179,11 +179,13 @@ spec:
|
||||
serviceAccountName: multus
|
||||
containers:
|
||||
- name: kube-multus
|
||||
image: ghcr.io/k8snetworkplumbingwg/multus-cni:stable
|
||||
command: ["/entrypoint.sh"]
|
||||
image: ghcr.io/k8snetworkplumbingwg/multus-cni:snapshot
|
||||
command: ["/thin_entrypoint"]
|
||||
args:
|
||||
- "--multus-conf-file=auto"
|
||||
- "--cni-version=0.3.1"
|
||||
- "--multus-autoconfig-dir=/host/etc/cni/net.d"
|
||||
- "--cni-conf-dir=/host/etc/cni/net.d"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
@ -202,11 +204,11 @@ spec:
|
||||
mountPath: /tmp/multus-conf
|
||||
initContainers:
|
||||
- name: install-multus-binary
|
||||
image: ghcr.io/k8snetworkplumbingwg/multus-cni:stable
|
||||
command:
|
||||
- "cp"
|
||||
- "/usr/src/multus-cni/bin/multus"
|
||||
- "/host/opt/cni/bin/multus"
|
||||
image: ghcr.io/k8snetworkplumbingwg/multus-cni:snapshot
|
||||
command: ["/install_multus"]
|
||||
args:
|
||||
- "--type"
|
||||
- "thin"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "10m"
|
||||
|
@ -145,7 +145,7 @@ spec:
|
||||
containers:
|
||||
- name: kube-multus
|
||||
image: localhost:5000/multus:e2e
|
||||
command: ["/entrypoint.sh"]
|
||||
command: ["/thin_entrypoint"]
|
||||
args:
|
||||
- "--multus-conf-file=auto"
|
||||
- "--force-cni-version=true"
|
||||
@ -169,10 +169,10 @@ spec:
|
||||
initContainers:
|
||||
- name: install-multus-binary
|
||||
image: localhost:5000/multus:e2e
|
||||
command:
|
||||
- "cp"
|
||||
- "/usr/src/multus-cni/bin/multus"
|
||||
- "/host/opt/cni/bin/multus"
|
||||
command: ["/install_multus"]
|
||||
args:
|
||||
- "--type"
|
||||
- "thin"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "10m"
|
||||
|
@ -59,3 +59,7 @@ echo "Building multus-daemon"
|
||||
go build -o "${DEST_DIR}"/multus-daemon -ldflags "${LDFLAGS}" ./cmd/multus-daemon
|
||||
echo "Building multus-shim"
|
||||
go build -o "${DEST_DIR}"/multus-shim -ldflags "${LDFLAGS}" ./cmd/multus-shim
|
||||
echo "Building install_multus"
|
||||
go build -o "${DEST_DIR}"/install_multus -ldflags "${LDFLAGS}" ./cmd/install_multus
|
||||
echo "Building thin_entrypoint"
|
||||
go build -o "${DEST_DIR}"/thin_entrypoint -ldflags "${LDFLAGS}" ./cmd/thin_entrypoint
|
||||
|
@ -7,11 +7,12 @@ ADD . /usr/src/multus-cni
|
||||
RUN cd /usr/src/multus-cni && \
|
||||
./hack/build-go.sh
|
||||
|
||||
FROM python:slim
|
||||
FROM gcr.io/distroless/base-debian11
|
||||
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
|
||||
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
|
||||
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
|
||||
WORKDIR /
|
||||
|
||||
ADD ./images/entrypoint.sh /
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
COPY --from=build /usr/src/multus-cni/bin/install_multus /
|
||||
COPY --from=build /usr/src/multus-cni/bin/thin_entrypoint /
|
||||
ENTRYPOINT ["/thin_entrypoint"]
|
||||
|
@ -11,12 +11,12 @@ RUN cd /usr/src/multus-cni && \
|
||||
./hack/build-go.sh
|
||||
|
||||
# build arm container
|
||||
FROM arm32v7/python:slim
|
||||
FROM gcr.io/distroless/base-debian11:latest-arm
|
||||
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
|
||||
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
|
||||
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
|
||||
|
||||
WORKDIR /
|
||||
ADD ./images/entrypoint.sh /
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
COPY --from=build /usr/src/multus-cni/bin/install_multus /
|
||||
COPY --from=build /usr/src/multus-cni/bin/thin_entrypoint /
|
||||
ENTRYPOINT ["/thin_entrypoint"]
|
||||
|
@ -11,12 +11,12 @@ RUN cd /usr/src/multus-cni && \
|
||||
./hack/build-go.sh
|
||||
|
||||
# build arm64 container
|
||||
FROM arm64v8/python:slim
|
||||
FROM gcr.io/distroless/base-debian11:latest-arm64
|
||||
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
|
||||
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
|
||||
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
|
||||
|
||||
WORKDIR /
|
||||
ADD ./images/entrypoint.sh /
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
COPY --from=build /usr/src/multus-cni/bin/install_multus /
|
||||
COPY --from=build /usr/src/multus-cni/bin/thin_entrypoint /
|
||||
ENTRYPOINT ["/thin_entrypoint"]
|
||||
|
@ -12,11 +12,12 @@ FROM quay.io/openshift/origin-base:latest
|
||||
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
|
||||
RUN mkdir -p /usr/src/multus-cni/images && mkdir -p /usr/src/multus-cni/bin
|
||||
COPY --from=builder /usr/src/multus-cni/bin/multus /usr/src/multus-cni/bin
|
||||
ADD ./images/entrypoint.sh /
|
||||
COPY --from=builder /usr/src/multus-cni/bin/install_multus /
|
||||
COPY --from=builder /usr/src/multus-cni/bin/thin_entrypoint /
|
||||
|
||||
LABEL io.k8s.display-name="Multus CNI" \
|
||||
io.k8s.description="This is a component of OpenShift Container Platform and provides a meta CNI plugin." \
|
||||
io.openshift.tags="openshift" \
|
||||
maintainer="Doug Smith <dosmith@redhat.com>"
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
ENTRYPOINT ["/thin_entrypoint"]
|
||||
|
@ -11,12 +11,12 @@ RUN cd /usr/src/multus-cni && \
|
||||
./hack/build-go.sh
|
||||
|
||||
# build ppc container
|
||||
FROM ppc64le/python:slim
|
||||
FROM gcr.io/distroless/base-debian11:latest-ppc64le
|
||||
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
|
||||
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
|
||||
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
|
||||
|
||||
WORKDIR /
|
||||
ADD ./images/entrypoint.sh /
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
COPY --from=build /usr/src/multus-cni/bin/install_multus /
|
||||
COPY --from=build /usr/src/multus-cni/bin/thin_entrypoint /
|
||||
ENTRYPOINT ["/thin_entrypoint"]
|
||||
|
@ -11,11 +11,12 @@ RUN cd /usr/src/multus-cni && \
|
||||
./hack/build-go.sh
|
||||
|
||||
# build s390x container
|
||||
FROM s390x/python:slim
|
||||
FROM gcr.io/distroless/base-debian11:latest-s390x
|
||||
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
|
||||
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
|
||||
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
|
||||
WORKDIR /
|
||||
ADD ./images/entrypoint.sh /
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
WORKDIR /
|
||||
COPY --from=build /usr/src/multus-cni/bin/install_multus /
|
||||
COPY --from=build /usr/src/multus-cni/bin/thin_entrypoint /
|
||||
ENTRYPOINT ["/thin_entrypoint"]
|
||||
|
@ -1,478 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# multus thin plugin install shell script
|
||||
#
|
||||
# note: this script is designed for quick-install or just 'tasting multus' in your test environment.
|
||||
# hence it does not cover advanced Kubernetes cluster operation (update, uninstall and so on).
|
||||
|
||||
# Always exit on errors.
|
||||
set -e
|
||||
|
||||
# Trap sigterm
|
||||
function exitonsigterm() {
|
||||
echo "Trapped sigterm, exiting."
|
||||
exit 0
|
||||
}
|
||||
trap exitonsigterm SIGTERM
|
||||
|
||||
# Set our known directories.
|
||||
CNI_CONF_DIR="/host/etc/cni/net.d"
|
||||
CNI_BIN_DIR="/host/opt/cni/bin"
|
||||
ADDITIONAL_BIN_DIR=""
|
||||
MULTUS_CONF_FILE="/usr/src/multus-cni/images/70-multus.conf"
|
||||
MULTUS_AUTOCONF_DIR="/host/etc/cni/net.d"
|
||||
MULTUS_BIN_FILE="/usr/src/multus-cni/bin/multus"
|
||||
MULTUS_KUBECONFIG_FILE_HOST="/etc/cni/net.d/multus.d/multus.kubeconfig"
|
||||
MULTUS_TEMP_KUBECONFIG="/tmp/multus.kubeconfig"
|
||||
MULTUS_MASTER_CNI_FILE_NAME=""
|
||||
MULTUS_NAMESPACE_ISOLATION=false
|
||||
MULTUS_GLOBAL_NAMESPACES=""
|
||||
MULTUS_LOG_TO_STDERR=true
|
||||
MULTUS_LOG_LEVEL=""
|
||||
MULTUS_LOG_FILE=""
|
||||
MULTUS_READINESS_INDICATOR_FILE=""
|
||||
OVERRIDE_NETWORK_NAME=false
|
||||
MULTUS_CLEANUP_CONFIG_ON_EXIT=false
|
||||
RENAME_SOURCE_CONFIG_FILE=false
|
||||
SKIP_BINARY_COPY=false
|
||||
FORCE_CNI_VERSION=false # force-cni-version is only for e2e-kind.
|
||||
|
||||
# Give help text for parameters.
|
||||
function usage()
|
||||
{
|
||||
echo -e "This is an entrypoint script for Multus CNI to overlay its configuration into"
|
||||
echo -e "locations in a filesystem. The configuration file will be copied to the"
|
||||
echo -e "corresponding configuration directory. When '--multus-conf-file=auto' is used,"
|
||||
echo -e "00-multus.conf will be automatically generated from the CNI configuration file"
|
||||
echo -e "of the master plugin (the first file in lexicographical order in cni-conf-dir)."
|
||||
echo -e "When '--multus-master-cni-file-name' is used, 00-multus.conf will be"
|
||||
echo -e "automatically generated from the specific file rather than the first file."
|
||||
echo -e ""
|
||||
echo -e "./entrypoint.sh"
|
||||
echo -e "\t-h --help"
|
||||
echo -e "\t--cni-bin-dir=$CNI_BIN_DIR"
|
||||
echo -e "\t--cni-conf-dir=$CNI_CONF_DIR"
|
||||
echo -e "\t--cni-version=<cniVersion (e.g. 0.3.1)>"
|
||||
echo -e "\t--multus-conf-file=$MULTUS_CONF_FILE"
|
||||
echo -e "\t--multus-bin-file=$MULTUS_BIN_FILE"
|
||||
echo -e "\t--skip-multus-binary-copy=$SKIP_BINARY_COPY"
|
||||
echo -e "\t--multus-kubeconfig-file-host=$MULTUS_KUBECONFIG_FILE_HOST"
|
||||
echo -e "\t--multus-master-cni-file-name=$MULTUS_MASTER_CNI_FILE_NAME (empty by default, example: 10-calico.conflist)"
|
||||
echo -e "\t--namespace-isolation=$MULTUS_NAMESPACE_ISOLATION"
|
||||
echo -e "\t--global-namespaces=$MULTUS_GLOBAL_NAMESPACES (used only with --namespace-isolation=true)"
|
||||
echo -e "\t--multus-autoconfig-dir=$MULTUS_AUTOCONF_DIR (used only with --multus-conf-file=auto)"
|
||||
echo -e "\t--multus-log-to-stderr=$MULTUS_LOG_TO_STDERR (empty by default, used only with --multus-conf-file=auto)"
|
||||
echo -e "\t--multus-log-level=$MULTUS_LOG_LEVEL (empty by default, used only with --multus-conf-file=auto)"
|
||||
echo -e "\t--multus-log-file=$MULTUS_LOG_FILE (empty by default, used only with --multus-conf-file=auto)"
|
||||
echo -e "\t--override-network-name=false (used only with --multus-conf-file=auto)"
|
||||
echo -e "\t--cleanup-config-on-exit=false (used only with --multus-conf-file=auto)"
|
||||
echo -e "\t--rename-conf-file=false (used only with --multus-conf-file=auto)"
|
||||
echo -e "\t--readiness-indicator-file=$MULTUS_READINESS_INDICATOR_FILE (used only with --multus-conf-file=auto)"
|
||||
echo -e "\t--additional-bin-dir=$ADDITIONAL_BIN_DIR (adds binDir option to configuration, used only with --multus-conf-file=auto)"
|
||||
}
|
||||
|
||||
function log()
|
||||
{
|
||||
echo "$(date --iso-8601=seconds) ${1}"
|
||||
}
|
||||
|
||||
function error()
|
||||
{
|
||||
log "ERR: {$1}"
|
||||
}
|
||||
|
||||
function warn()
|
||||
{
|
||||
log "WARN: {$1}"
|
||||
}
|
||||
|
||||
function checkCniVersion {
|
||||
cniversion_python_tmpfile=$(mktemp)
|
||||
cat << EOF > $cniversion_python_tmpfile
|
||||
import json, sys
|
||||
|
||||
def version(v):
|
||||
return [int(x) for x in v.split(".")]
|
||||
|
||||
v_040 = version("0.4.0")
|
||||
v_top_level = sys.argv[2]
|
||||
with open(sys.argv[1], "r") as f:
|
||||
v_nested = json.load(f)["cniVersion"]
|
||||
if version(v_top_level) >= v_040 and version(v_nested) < v_040:
|
||||
msg = "Multus cni version is %s while master plugin cni version is %s"
|
||||
print(msg % (v_top_level, v_nested))
|
||||
EOF
|
||||
python3 $cniversion_python_tmpfile $1 $2
|
||||
}
|
||||
|
||||
# Parse parameters given as arguments to this script.
|
||||
while [ "$1" != "" ]; do
|
||||
PARAM=`echo $1 | awk -F= '{print $1}'`
|
||||
VALUE=`echo $1 | awk -F= '{print $2}'`
|
||||
case $PARAM in
|
||||
-h | --help)
|
||||
usage
|
||||
exit
|
||||
;;
|
||||
--cni-version)
|
||||
CNI_VERSION=$VALUE
|
||||
;;
|
||||
# force-cni-version is only for e2e-kind testing
|
||||
--force-cni-version)
|
||||
FORCE_CNI_VERSION=$VALUE
|
||||
;;
|
||||
--cni-bin-dir)
|
||||
CNI_BIN_DIR=$VALUE
|
||||
;;
|
||||
--cni-conf-dir)
|
||||
CNI_CONF_DIR=$VALUE
|
||||
;;
|
||||
--cni-bin-dir)
|
||||
CNI_BIN_DIR=$VALUE
|
||||
;;
|
||||
--multus-conf-file)
|
||||
MULTUS_CONF_FILE=$VALUE
|
||||
;;
|
||||
--multus-kubeconfig-file-host)
|
||||
MULTUS_KUBECONFIG_FILE_HOST=$VALUE
|
||||
;;
|
||||
--multus-master-cni-file-name)
|
||||
MULTUS_MASTER_CNI_FILE_NAME=$VALUE
|
||||
;;
|
||||
--namespace-isolation)
|
||||
MULTUS_NAMESPACE_ISOLATION=$VALUE
|
||||
;;
|
||||
--global-namespaces)
|
||||
MULTUS_GLOBAL_NAMESPACES=$VALUE
|
||||
;;
|
||||
--multus-log-to-stderr)
|
||||
MULTUS_LOG_TO_STDERR=$VALUE
|
||||
;;
|
||||
--multus-log-level)
|
||||
MULTUS_LOG_LEVEL=$VALUE
|
||||
;;
|
||||
--multus-log-file)
|
||||
MULTUS_LOG_FILE=$VALUE
|
||||
;;
|
||||
--multus-autoconfig-dir)
|
||||
MULTUS_AUTOCONF_DIR=$VALUE
|
||||
;;
|
||||
--override-network-name)
|
||||
OVERRIDE_NETWORK_NAME=$VALUE
|
||||
;;
|
||||
--cleanup-config-on-exit)
|
||||
MULTUS_CLEANUP_CONFIG_ON_EXIT=$VALUE
|
||||
;;
|
||||
--rename-conf-file)
|
||||
RENAME_SOURCE_CONFIG_FILE=$VALUE
|
||||
;;
|
||||
--additional-bin-dir)
|
||||
ADDITIONAL_BIN_DIR=$VALUE
|
||||
;;
|
||||
--skip-multus-binary-copy)
|
||||
SKIP_BINARY_COPY=$VALUE
|
||||
;;
|
||||
--readiness-indicator-file)
|
||||
MULTUS_READINESS_INDICATOR_FILE=$VALUE
|
||||
;;
|
||||
*)
|
||||
warn "unknown parameter \"$PARAM\""
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
|
||||
# Create array of known locations
|
||||
declare -a arr=($CNI_CONF_DIR $CNI_BIN_DIR $MULTUS_BIN_FILE)
|
||||
if [ "$MULTUS_CONF_FILE" != "auto" ]; then
|
||||
arr+=($MULTUS_CONF_FILE)
|
||||
fi
|
||||
|
||||
|
||||
# Loop through and verify each location each.
|
||||
for i in "${arr[@]}"
|
||||
do
|
||||
if [ ! -e "$i" ]; then
|
||||
warn "Location $i does not exist"
|
||||
exit 1;
|
||||
fi
|
||||
done
|
||||
|
||||
# Copy files into place and atomically move into final binary name
|
||||
if [ "$SKIP_BINARY_COPY" = false ]; then
|
||||
cp -f $MULTUS_BIN_FILE $CNI_BIN_DIR/_multus
|
||||
mv -f $CNI_BIN_DIR/_multus $CNI_BIN_DIR/multus
|
||||
else
|
||||
log "Entrypoint skipped copying Multus binary."
|
||||
fi
|
||||
|
||||
if [ "$MULTUS_CONF_FILE" != "auto" ]; then
|
||||
cp -f $MULTUS_CONF_FILE $CNI_CONF_DIR
|
||||
fi
|
||||
|
||||
# Make a multus.d directory (for our kubeconfig)
|
||||
|
||||
mkdir -p $CNI_CONF_DIR/multus.d
|
||||
MULTUS_KUBECONFIG=$CNI_CONF_DIR/multus.d/multus.kubeconfig
|
||||
|
||||
# ------------------------------- Generate a "kube-config"
|
||||
# Inspired by: https://tinyurl.com/y7r2knme
|
||||
SERVICE_ACCOUNT_PATH=/run/secrets/kubernetes.io/serviceaccount
|
||||
KUBE_CA_FILE=${KUBE_CA_FILE:-$SERVICE_ACCOUNT_PATH/ca.crt}
|
||||
SERVICEACCOUNT_TOKEN=$(cat $SERVICE_ACCOUNT_PATH/token)
|
||||
SKIP_TLS_VERIFY=${SKIP_TLS_VERIFY:-false}
|
||||
|
||||
|
||||
# Check if we're running as a k8s pod.
|
||||
if [ -f "$SERVICE_ACCOUNT_PATH/token" ]; then
|
||||
# We're running as a k8d pod - expect some variables.
|
||||
if [ -z ${KUBERNETES_SERVICE_HOST} ]; then
|
||||
error "KUBERNETES_SERVICE_HOST not set"; exit 1;
|
||||
fi
|
||||
if [ -z ${KUBERNETES_SERVICE_PORT} ]; then
|
||||
error "KUBERNETES_SERVICE_PORT not set"; exit 1;
|
||||
fi
|
||||
|
||||
if [ "$SKIP_TLS_VERIFY" == "true" ]; then
|
||||
TLS_CFG="insecure-skip-tls-verify: true"
|
||||
elif [ -f "$KUBE_CA_FILE" ]; then
|
||||
TLS_CFG="certificate-authority-data: $(cat $KUBE_CA_FILE | base64 | tr -d '\n')"
|
||||
fi
|
||||
|
||||
# Write a kubeconfig file for the CNI plugin. Do this
|
||||
# to skip TLS verification for now. We should eventually support
|
||||
# writing more complete kubeconfig files. This is only used
|
||||
# if the provided CNI network config references it.
|
||||
touch $MULTUS_TEMP_KUBECONFIG
|
||||
chmod ${KUBECONFIG_MODE:-600} $MULTUS_TEMP_KUBECONFIG
|
||||
# Write the kubeconfig to a temp file first.
|
||||
cat > $MULTUS_TEMP_KUBECONFIG <<EOF
|
||||
# Kubeconfig file for Multus CNI plugin.
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- name: local
|
||||
cluster:
|
||||
server: ${KUBERNETES_SERVICE_PROTOCOL:-https}://[${KUBERNETES_SERVICE_HOST}]:${KUBERNETES_SERVICE_PORT}
|
||||
$TLS_CFG
|
||||
users:
|
||||
- name: multus
|
||||
user:
|
||||
token: "${SERVICEACCOUNT_TOKEN}"
|
||||
contexts:
|
||||
- name: multus-context
|
||||
context:
|
||||
cluster: local
|
||||
user: multus
|
||||
current-context: multus-context
|
||||
EOF
|
||||
|
||||
# Atomically move the temp kubeconfig to its permanent home.
|
||||
mv -f $MULTUS_TEMP_KUBECONFIG $MULTUS_KUBECONFIG
|
||||
|
||||
else
|
||||
warn "Doesn't look like we're running in a kubernetes environment (no serviceaccount token)"
|
||||
fi
|
||||
|
||||
# ---------------------- end Generate a "kube-config".
|
||||
|
||||
# ------------------------------- Generate "00-multus.conf"
|
||||
|
||||
function generateMultusConf {
|
||||
if [ "$MULTUS_CONF_FILE" == "auto" ]; then
|
||||
log "Generating Multus configuration file using files in $MULTUS_AUTOCONF_DIR..."
|
||||
found_master=false
|
||||
tries=0
|
||||
while [ $found_master == false ]; do
|
||||
if [ "$MULTUS_MASTER_CNI_FILE_NAME" != "" ]; then
|
||||
MASTER_PLUGIN="$MULTUS_MASTER_CNI_FILE_NAME"
|
||||
if [ ! -f "$MULTUS_AUTOCONF_DIR/$MASTER_PLUGIN" ]; then
|
||||
error "Cannot find master cni file $MULTUS_AUTOCONF_DIR/$MASTER_PLUGIN"
|
||||
exit 1;
|
||||
fi
|
||||
else
|
||||
MASTER_PLUGIN="$(ls $MULTUS_AUTOCONF_DIR | grep -E '\.conf(list)?$' | grep -Ev '00-multus\.conf' | head -1)"
|
||||
fi
|
||||
if [ "$MASTER_PLUGIN" == "" ]; then
|
||||
if [ $tries -lt 600 ]; then
|
||||
if ! (($tries % 5)); then
|
||||
log "Attempting to find master plugin configuration, attempt $tries"
|
||||
fi
|
||||
let "tries+=1"
|
||||
sleep 1;
|
||||
else
|
||||
error "Multus could not be configured: no master plugin was found."
|
||||
exit 1;
|
||||
fi
|
||||
else
|
||||
log "Using MASTER_PLUGIN: $MASTER_PLUGIN"
|
||||
|
||||
found_master=true
|
||||
|
||||
ISOLATION_STRING=""
|
||||
if [ "$MULTUS_NAMESPACE_ISOLATION" == true ]; then
|
||||
ISOLATION_STRING="\"namespaceIsolation\": true,"
|
||||
fi
|
||||
|
||||
GLOBAL_NAMESPACES_STRING=""
|
||||
if [ ! -z "${MULTUS_GLOBAL_NAMESPACES// }" ]; then
|
||||
GLOBAL_NAMESPACES_STRING="\"globalNamespaces\": \"$MULTUS_GLOBAL_NAMESPACES\","
|
||||
fi
|
||||
|
||||
LOG_TO_STDERR_STRING=""
|
||||
if [ "$MULTUS_LOG_TO_STDERR" == false ]; then
|
||||
LOG_TO_STDERR_STRING="\"logToStderr\": false,"
|
||||
fi
|
||||
|
||||
|
||||
LOG_LEVEL_STRING=""
|
||||
if [ ! -z "${MULTUS_LOG_LEVEL// }" ]; then
|
||||
case "$MULTUS_LOG_LEVEL" in
|
||||
debug)
|
||||
;;
|
||||
error)
|
||||
;;
|
||||
panic)
|
||||
;;
|
||||
verbose)
|
||||
;;
|
||||
*)
|
||||
error "Log levels should be one of: debug/verbose/error/panic, did not understand $MULTUS_LOG_LEVEL"
|
||||
usage
|
||||
exit 1
|
||||
esac
|
||||
LOG_LEVEL_STRING="\"logLevel\": \"$MULTUS_LOG_LEVEL\","
|
||||
fi
|
||||
|
||||
LOG_FILE_STRING=""
|
||||
if [ ! -z "${MULTUS_LOG_FILE// }" ]; then
|
||||
LOG_FILE_STRING="\"logFile\": \"$MULTUS_LOG_FILE\","
|
||||
fi
|
||||
|
||||
CNI_VERSION_STRING=""
|
||||
if [ ! -z "${CNI_VERSION// }" ]; then
|
||||
CNI_VERSION_STRING="\"cniVersion\": \"$CNI_VERSION\","
|
||||
fi
|
||||
|
||||
ADDITIONAL_BIN_DIR_STRING=""
|
||||
if [ ! -z "${ADDITIONAL_BIN_DIR// }" ]; then
|
||||
ADDITIONAL_BIN_DIR_STRING="\"binDir\": \"$ADDITIONAL_BIN_DIR\","
|
||||
fi
|
||||
|
||||
|
||||
READINESS_INDICATOR_FILE_STRING=""
|
||||
if [ ! -z "${MULTUS_READINESS_INDICATOR_FILE// }" ]; then
|
||||
READINESS_INDICATOR_FILE_STRING="\"readinessindicatorfile\": \"$MULTUS_READINESS_INDICATOR_FILE\","
|
||||
fi
|
||||
|
||||
if [ "$OVERRIDE_NETWORK_NAME" == "true" ]; then
|
||||
MASTER_PLUGIN_NET_NAME="$(cat $MULTUS_AUTOCONF_DIR/$MASTER_PLUGIN | \
|
||||
python3 -c 'import json,sys;print(json.load(sys.stdin)["name"])')"
|
||||
else
|
||||
MASTER_PLUGIN_NET_NAME="multus-cni-network"
|
||||
fi
|
||||
|
||||
capabilities_python_filter_tmpfile=$(mktemp)
|
||||
cat << EOF > $capabilities_python_filter_tmpfile
|
||||
import json,sys
|
||||
conf = json.load(sys.stdin)
|
||||
capabilities = {}
|
||||
if 'plugins' in conf:
|
||||
for capa in [p['capabilities'] for p in conf['plugins'] if 'capabilities' in p]:
|
||||
capabilities.update({capability:enabled for (capability,enabled) in capa.items() if enabled})
|
||||
elif 'capabilities' in conf:
|
||||
capabilities.update({capability:enabled for (capability,enabled) in conf['capabilities'] if enabled})
|
||||
if len(capabilities) > 0:
|
||||
print("""\"capabilities\": """ + json.dumps(capabilities) + ",")
|
||||
else:
|
||||
print("")
|
||||
EOF
|
||||
|
||||
NESTED_CAPABILITIES_STRING="$(cat $MULTUS_AUTOCONF_DIR/$MASTER_PLUGIN | \
|
||||
python3 $capabilities_python_filter_tmpfile)"
|
||||
rm $capabilities_python_filter_tmpfile
|
||||
log "Nested capabilities string: $NESTED_CAPABILITIES_STRING"
|
||||
|
||||
MASTER_PLUGIN_LOCATION=$MULTUS_AUTOCONF_DIR/$MASTER_PLUGIN
|
||||
if [ "$FORCE_CNI_VERSION" == true ]; then
|
||||
MASTER_PLUGIN_JSON="$(cat $MASTER_PLUGIN_LOCATION | sed -e "s/\"cniVersion.*/\"cniVersion\": \"$CNI_VERSION\",/g")"
|
||||
else
|
||||
MASTER_PLUGIN_JSON="$(cat $MASTER_PLUGIN_LOCATION)"
|
||||
log "Using $MASTER_PLUGIN_LOCATION as a source to generate the Multus configuration"
|
||||
CHECK_CNI_VERSION=$(checkCniVersion $MASTER_PLUGIN_LOCATION $CNI_VERSION)
|
||||
if [ "$CHECK_CNI_VERSION" != "" ] ; then
|
||||
error "$CHECK_CNI_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
CONF=$(cat <<-EOF
|
||||
{
|
||||
$CNI_VERSION_STRING
|
||||
"name": "$MASTER_PLUGIN_NET_NAME",
|
||||
"type": "multus",
|
||||
$NESTED_CAPABILITIES_STRING
|
||||
$ISOLATION_STRING
|
||||
$GLOBAL_NAMESPACES_STRING
|
||||
$LOG_TO_STDERR_STRING
|
||||
$LOG_LEVEL_STRING
|
||||
$LOG_FILE_STRING
|
||||
$ADDITIONAL_BIN_DIR_STRING
|
||||
$READINESS_INDICATOR_FILE_STRING
|
||||
"kubeconfig": "$MULTUS_KUBECONFIG_FILE_HOST",
|
||||
"delegates": [
|
||||
$MASTER_PLUGIN_JSON
|
||||
]
|
||||
}
|
||||
EOF
|
||||
)
|
||||
tmpfile=$(mktemp)
|
||||
echo $CONF > $tmpfile
|
||||
mv $tmpfile $CNI_CONF_DIR/00-multus.conf
|
||||
log "Config file created @ $CNI_CONF_DIR/00-multus.conf"
|
||||
echo $CONF
|
||||
|
||||
# If we're not performing the cleanup on exit, we can safely rename the config file.
|
||||
if [ "$RENAME_SOURCE_CONFIG_FILE" == true ]; then
|
||||
mv ${MULTUS_AUTOCONF_DIR}/${MASTER_PLUGIN} ${MULTUS_AUTOCONF_DIR}/${MASTER_PLUGIN}.old
|
||||
log "Original master file moved to ${MULTUS_AUTOCONF_DIR}/${MASTER_PLUGIN}.old"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
generateMultusConf
|
||||
|
||||
# ---------------------- end Generate "00-multus.conf".
|
||||
|
||||
# Enter either sleep loop, or watch loop...
|
||||
if [ "$MULTUS_CLEANUP_CONFIG_ON_EXIT" == true ]; then
|
||||
log "Entering watch loop..."
|
||||
while true; do
|
||||
# Check and see if the original master plugin configuration exists...
|
||||
if [ ! -f "$MASTER_PLUGIN_LOCATION" ]; then
|
||||
log "Master plugin @ $MASTER_PLUGIN_LOCATION has been deleted. Allowing 45 seconds for its restoration..."
|
||||
sleep 10
|
||||
for i in {1..35}
|
||||
do
|
||||
if [ -f "$MASTER_PLUGIN_LOCATION" ]; then
|
||||
log "Master plugin @ $MASTER_PLUGIN_LOCATION was restored. Regenerating given configuration."
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
generateMultusConf
|
||||
log "Continuing watch loop after configuration regeneration..."
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
else
|
||||
log "Entering sleep (success)..."
|
||||
if tty -s; then
|
||||
read
|
||||
else
|
||||
sleep infinity
|
||||
fi
|
||||
fi
|
28
pkg/cmdutils/cmdutils_suite_test.go
Normal file
28
pkg/cmdutils/cmdutils_suite_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2023 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 cmdutils is the package that contains utilities for multus command
|
||||
package cmdutils
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestServer(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "cmdutils")
|
||||
}
|
84
pkg/cmdutils/utils.go
Normal file
84
pkg/cmdutils/utils.go
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2023 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 cmdutils is the package that contains utilities for multus command
|
||||
package cmdutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// CopyFileAtomic does file copy atomically
|
||||
func CopyFileAtomic(srcFilePath, destDir, tempFileName, destFileName string) error {
|
||||
tempFilePath := filepath.Join(destDir, tempFileName)
|
||||
// check temp filepath and remove old file if exists
|
||||
if _, err := os.Stat(tempFilePath); err == nil {
|
||||
err = os.Remove(tempFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot remove old temp file %q: %v", tempFilePath, err)
|
||||
}
|
||||
}
|
||||
|
||||
// create temp file
|
||||
f, err := os.CreateTemp(destDir, tempFileName)
|
||||
defer f.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create temp file %q in %q: %v", tempFileName, destDir, err)
|
||||
}
|
||||
|
||||
srcFile, err := os.Open(srcFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot open file %q: %v", srcFilePath, err)
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
// Copy file to tempfile
|
||||
_, err = io.Copy(f, srcFile)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
os.Remove(tempFilePath)
|
||||
return fmt.Errorf("cannot write data to temp file %q: %v", tempFilePath, err)
|
||||
}
|
||||
if err := f.Sync(); err != nil {
|
||||
return fmt.Errorf("cannot flush temp file %q: %v", tempFilePath, err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
return fmt.Errorf("cannot close temp file %q: %v", tempFilePath, err)
|
||||
}
|
||||
|
||||
// change file mode if different
|
||||
destFilePath := filepath.Join(destDir, destFileName)
|
||||
_, err = os.Stat(destFilePath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
srcFileStat, err := os.Stat(srcFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Chmod(f.Name(), srcFileStat.Mode()); err != nil {
|
||||
return fmt.Errorf("cannot set stat on temp file %q: %v", f.Name(), err)
|
||||
}
|
||||
|
||||
// replace file with tempfile
|
||||
if err := os.Rename(f.Name(), destFilePath); err != nil {
|
||||
return fmt.Errorf("cannot replace %q with temp file %q: %v", destFilePath, tempFilePath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
70
pkg/cmdutils/utils_test.go
Normal file
70
pkg/cmdutils/utils_test.go
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2023 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 cmdutils is the package that contains utilities for multus command
|
||||
package cmdutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("thin entrypoint testing", func() {
|
||||
It("Run CopyFileAtomic()", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// create source directory
|
||||
srcDir := fmt.Sprintf("%s/src", tmpDir)
|
||||
err = os.Mkdir(srcDir, 0755)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// create destination directory
|
||||
destDir := fmt.Sprintf("%s/dest", tmpDir)
|
||||
err = os.Mkdir(destDir, 0755)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// sample source file
|
||||
srcFilePath := fmt.Sprintf("%s/sampleInput", srcDir)
|
||||
err = os.WriteFile(srcFilePath, []byte("sampleInputABC"), 0744)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// old files in dest
|
||||
destFileName := "sampleInputDest"
|
||||
destFilePath := fmt.Sprintf("%s/%s", destDir, destFileName)
|
||||
err = os.WriteFile(destFilePath, []byte("inputOldXYZ"), 0611)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
tempFileName := "temp_file"
|
||||
err = CopyFileAtomic(srcFilePath, destDir, tempFileName, destFileName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// check file mode
|
||||
stat, err := os.Stat(destFilePath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(stat.Mode()).To(Equal(os.FileMode(0744)))
|
||||
|
||||
// check file contents
|
||||
destFileByte, err := os.ReadFile(destFilePath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(destFileByte).To(Equal([]byte("sampleInputABC")))
|
||||
|
||||
err = os.RemoveAll(tmpDir)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user