forked from rook/rook
Add a more involved multus validation test to the Rook binary. Because this is intended to be end-user runnable, make sure operator-only commands are hidden. Build this into the rook binary instead of creating a separate binary for ease, and because any binary built with the kube api becomes 40+ megabytes. We save quite a bit of space by including this in the Rook binary, which is good for keeping container layers as small as possible. Signed-off-by: Blaine Gardner <blaine.gardner@redhat.com>
130 lines
4.2 KiB
Go
130 lines
4.2 KiB
Go
package multus
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
nadutils "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils"
|
|
core "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
)
|
|
|
|
// Logger defines the log types that this library will use for outputting status information to the
|
|
// user. Because this library may be called interactively by a user or programmatically by another
|
|
// application, we allow the calling application to make a very simple Logger type that will be
|
|
// compatible with this library.
|
|
type Logger interface {
|
|
Infof(format string, args ...interface{})
|
|
Debugf(format string, args ...interface{})
|
|
Warningf(format string, args ...interface{})
|
|
}
|
|
|
|
type SimpleStderrLogger struct{}
|
|
|
|
func (*SimpleStderrLogger) Infof(format string, args ...interface{}) {
|
|
fmt.Fprintf(os.Stderr, " INFO: "+format+"\n", args...)
|
|
}
|
|
|
|
func (*SimpleStderrLogger) Debugf(format string, args ...interface{}) {
|
|
fmt.Fprintf(os.Stderr, "DEBUG: "+format+"\n", args...)
|
|
}
|
|
|
|
func (*SimpleStderrLogger) Warningf(format string, args ...interface{}) {
|
|
fmt.Fprintf(os.Stderr, " WARN: "+format+"\n", args...)
|
|
}
|
|
|
|
func getNetworksFromPod(
|
|
pod *core.Pod,
|
|
desiredPublicNet, desiredClusterNet *types.NamespacedName,
|
|
) (
|
|
publicAddr, clusterAddr string, suggestions []string, err error,
|
|
) {
|
|
nets, err := nadutils.GetNetworkStatus(pod)
|
|
if err != nil {
|
|
return "", "", unableToProvideAddressSuggestions,
|
|
fmt.Errorf("pod has no network status yet: %w", err)
|
|
}
|
|
if len(nets) == 0 {
|
|
return "", "", unableToProvideAddressSuggestions,
|
|
fmt.Errorf("pod has no attached networks yet")
|
|
}
|
|
|
|
// if the pod has networks attached, parse all of them, and report any debugging suggestions
|
|
// associated for each one
|
|
publicAddr, clusterAddr = "", ""
|
|
suggestions = []string{}
|
|
for _, net := range nets {
|
|
nsName, err := networkNamespacedName(net.Name, pod.Namespace)
|
|
if err != nil {
|
|
suggestions = append(suggestions,
|
|
fmt.Sprintf("pod has an attached network with an un-parse-able network name [%s]; "+
|
|
"not sure what to do; this is unlikely to resolve", net.Name),
|
|
)
|
|
}
|
|
if len(net.IPs) == 0 {
|
|
suggestions = append(suggestions,
|
|
fmt.Sprintf("pod has an attached network [%s] with no IP; "+
|
|
"could this be an IPAM issue?", net.Name))
|
|
}
|
|
|
|
ip := net.IPs[0] // any IP should work
|
|
if desiredPublicNet != nil && nsName == *desiredPublicNet {
|
|
// FOUND DESIRED PUBLIC ADDR
|
|
publicAddr = ip
|
|
}
|
|
if desiredClusterNet != nil && nsName == *desiredClusterNet {
|
|
// FOUND DESIRED CLUSTER ADDR
|
|
clusterAddr = ip
|
|
}
|
|
}
|
|
|
|
if desiredPublicNet != nil && publicAddr == "" {
|
|
suggestions = append(suggestions, "pod does not have a public network attachment")
|
|
}
|
|
if desiredClusterNet != nil && clusterAddr == "" {
|
|
suggestions = append(suggestions, "pod does not have a cluster network attachment")
|
|
}
|
|
|
|
// if there are any suggestions, something isn't right
|
|
if len(suggestions) > 0 {
|
|
suggestions = append(suggestions, unableToProvideAddressSuggestions...)
|
|
return publicAddr, clusterAddr, suggestions, fmt.Errorf("did not find all desired networks")
|
|
}
|
|
|
|
// found what we were looking for
|
|
return publicAddr, clusterAddr, []string{}, nil
|
|
}
|
|
|
|
func podIsRunning(pod core.Pod) bool {
|
|
return pod.Status.Phase == core.PodRunning
|
|
}
|
|
|
|
func podIsReady(pod core.Pod) bool {
|
|
for _, c := range pod.Status.Conditions {
|
|
if c.Type == core.PodReady {
|
|
return c.Status == core.ConditionTrue
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func networkNamespacedName(netName, podNamespace string) (types.NamespacedName, error) {
|
|
nsName := types.NamespacedName{}
|
|
// what we really want is the result of parsePodNetworkObjectText(), a private method.
|
|
// because the network name is in the same format as network annotations, we can at
|
|
// at least use ParseNetworkAnnotation() to parse the network name
|
|
netSelections, err := nadutils.ParseNetworkAnnotation(netName, podNamespace)
|
|
if err != nil {
|
|
return nsName, fmt.Errorf("failed to parse network name %q into namespaced name: %w", netName, err)
|
|
}
|
|
|
|
if len(netSelections) != 1 {
|
|
return nsName, fmt.Errorf("failed to parse network name %q into a single namespaced network name, instead: %v", netName, netSelections)
|
|
}
|
|
|
|
net := netSelections[0]
|
|
nsName.Namespace = net.Namespace
|
|
nsName.Name = net.Name
|
|
return nsName, nil
|
|
}
|