Files
my-rook-config/cmd/rook/userfacing/multus/validation/validation.go
T
iPraveenParihar 78d6528153 test: allow specifying custom nginx image for multus validation
Allow overriding the nginx server image used for the web server and clients from CLI with --nginx-image flag.
Set default image in flag as 'nginxinc/nginx-unprivileged:stable-alpine'

Signed-off-by: iPraveenParihar <praveenparihar68@gmail.com>
2023-05-24 21:04:24 +05:30

229 lines
8.7 KiB
Go

/*
Copyright 2023 The Rook Authors. All rights reserved.
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 validation
import (
"context"
"fmt"
"os"
"strconv"
"time"
"github.com/coreos/pkg/capnslog"
"github.com/rook/rook/cmd/rook/rook"
"github.com/rook/rook/pkg/daemon/multus"
"github.com/rook/rook/pkg/operator/k8sutil"
"github.com/spf13/cobra"
)
// defaults
var (
DefaultValidationNamespace = "rook-ceph"
// 1 mon, 3 osds, 1 mgr, 1 mds, 1 nfs, 1 rgw, 1 rbdmirror, 1 cephfsmirror,
// (1 csi provisioner, 1 csi plugin) x3 for rbd, cephfs, and nfs CSI drivers
DefaultDaemonsPerNode = 16
DefaultTimeoutMinutes = 3
)
// config
var (
validationConfig = multus.ValidationTest{
Logger: capnslog.NewPackageLogger("github.com/rook/rook", "multus-validation"),
}
)
// commands
var (
// parent 'validation' command
Cmd = &cobra.Command{
Use: "validation",
Short: "Run and manage Multus validation tests for Rook",
}
// 'validation run' command
runCmd = &cobra.Command{
Use: "run [--public-network=<nad-name>] [--cluster-network=<nad-name>]",
Short: "Run a Multus validation test for Rook",
Long: `
Run a validation test that determines whether the current Multus and system
configurations will support Rook with Multus.
This is a fairly long-running test. It starts up a web server and many
clients to verify that Multus network communication works properly.
It does *not* perform any load testing. Networks that cannot support high
volumes of Ceph traffic may still encounter runtime issues. This may be
particularly noticeable with high I/O load or during OSD rebalancing
(see: https://docs.ceph.com/en/latest/architecture/#rebalancing).
`,
Run: func(cmd *cobra.Command, args []string) {
runValidation(cmd.Context())
},
Args: cobra.NoArgs,
}
// 'validation cleanup' command
cleanupCmd = &cobra.Command{
Use: "cleanup",
Short: "Clean up Multus validation test resources",
Run: func(cmd *cobra.Command, args []string) {
runCleanup(cmd.Context())
},
Args: cobra.NoArgs,
}
)
func init() {
validationConfig.Clientset = rook.GetInternalOrExternalClient()
Cmd.AddCommand(runCmd)
Cmd.AddCommand(cleanupCmd)
// flags on ALL subcommands - makes output more straightforward than using PersistentFlags() global flags on parent
for _, subCommand := range Cmd.Commands() {
// the default namespace is the current namespace the operator pod is running in if possible
defaultNamespace := os.Getenv(k8sutil.PodNamespaceEnvVar)
if defaultNamespace == "" {
defaultNamespace = DefaultValidationNamespace
}
subCommand.Flags().StringVarP(&validationConfig.Namespace, "namespace", "n", defaultNamespace,
"The namespace for validation test resources. "+
"It is recommended to set this to the namespace in which Rook's Ceph cluster will be installed.")
// VarPF() keeps the the specific var passed to it for setting at runtime, and the current
// val of that var when VarPF() is called is used as the default
validationConfig.ResourceTimeout = time.Duration(DefaultTimeoutMinutes) * time.Minute
t := (*timeoutMinutes)(&validationConfig.ResourceTimeout)
subCommand.Flags().VarPF(t, "timeout-minutes", "", /* no shorthand */
"The time to wait for resources to change to the expected state. For example, for the "+
"test web server to start, for test clients to become ready, or for test resources to be deleted. "+
"At longest, this may need to reflect the time it takes for client pods to to pull images, "+
"get address assignments, and then for each client to determine that its network connection is stable. "+
"Minimum: 1 minute. Recommended: 2 minutes or more.")
}
// flags for 'validation run'
runCmd.Flags().StringVar(&validationConfig.PublicNetwork, "public-network", "",
"The name of the Network Attachment Definition (NAD) that will be used for Ceph's public network. "+
"This should be a namespaced name in the form <namespace>/<name> if the NAD is defined in a different namespace from the cluster namespace.")
runCmd.Flags().StringVar(&validationConfig.ClusterNetwork, "cluster-network", "",
"The name of the Network Attachment Definition (NAD) that will be used for Ceph's cluster network. "+
"This should be a namespaced name in the form <namespace>/<name> if the NAD is defined in a different namespace from the cluster namespace.")
runCmd.Flags().IntVar(&validationConfig.DaemonsPerNode, "daemons-per-node", DefaultDaemonsPerNode,
"The number of validation test daemons to run per node. "+
"It is recommended to set this to the maximum number of Ceph daemons that can run on any node in the worst case of node failure(s). "+
"The default value is set to the worst-case value for a Rook Ceph cluster with 3 portable OSDs, 3 portable monitors, "+
"and where all optional child resources have been created with 1 daemon such that they all might run on a single node in a failure scenario. "+
"If you aren't sure what to choose for this value, add 1 for each additional OSD beyond 3.")
runCmd.Flags().StringVar(&validationConfig.NginxImage, "nginx-image", "nginxinc/nginx-unprivileged:stable-alpine",
"The Nginx image used for the validation server and clients.")
// flags for 'validation cleanup'
// none
}
func runValidation(ctx context.Context) {
if validationConfig.PublicNetwork == "" && validationConfig.ClusterNetwork == "" {
fmt.Print(`at least one of "--public-network" or "--cluster-network" must be specified`)
os.Exit(22 /* EINVAL */)
}
if validationConfig.NginxImage == "" {
fmt.Print(`--nginx-image must be specified`)
os.Exit(22 /* EINVAL */)
}
results, err := validationConfig.Run(ctx)
report := results.SuggestedDebuggingReport()
// success/failure message
fmt.Print("\n")
switch {
case err != nil:
fmt.Printf("RESULT: multus validation test failed: %v\n\n", err)
case report == "":
fmt.Print("RESULT: multus validation test succeeded!\n\n")
runCleanup(ctx)
os.Exit(0) // success!
case report != "":
// suggestions are bad
fmt.Print("RESULT: multus validation test succeeded, but there are suggestions\n\n")
}
// output report suggestions
fmt.Print(report + "\n")
// help users help us help them
fmt.Println("leaving multus validation test resources running for manual debugging")
fmt.Print(`
For assistance debugging, collect the following into an archive file:
- Output of this utility
- Network Attachment Definitions (NADs) used by this test
- A write-up describing the network configuration you are trying to achieve including the
intended network for Ceph public/client traffic, intended network for Ceph cluster traffic,
interface names and CIDRs for both networks, and any other details that are relevant.
- 'ifconfig' output from at least one Kubernetes worker node
- 'kubectl get pods -o wide' output from the test namespace
- 'kubectl describe pods' output from the test namespace
- 'kubectl get pods -o yaml' output from the test namespace
- 'kubectl get daemonsets' output from the test namespace
- 'kubectl describe daemonsets' output from the test namespace
- 'kubectl get daemonsets -o yaml' output from the test namespace
- 'kubectl logs multus-validation-test-web-server' output from the test namespace
- 'kubectl get nodes -o wide' output
`)
// tell them how to cleanup
fmt.Printf("\nTo clean up resources when you are done debugging: %s --namespace %s\n", cleanupCmd.CommandPath(), validationConfig.Namespace)
os.Exit(1)
}
func runCleanup(ctx context.Context) {
fmt.Printf("cleaning up multus validation test resources in namespace %q\n", validationConfig.Namespace)
results, err := validationConfig.CleanUp(ctx)
if err != nil {
fmt.Printf("multus validation test cleanup failed: %v\n\n", err)
fmt.Println(results.SuggestedDebuggingReport())
return
}
fmt.Print("multus validation test resources were successfully cleaned up\n")
}
// custom flag types
// implements pflag.Value interface to validate and set resource timeout and enforce nonzero
type timeoutMinutes time.Duration
func (t *timeoutMinutes) String() string { return time.Duration(*t).String() }
func (t *timeoutMinutes) Set(v string) error {
i, err := strconv.Atoi(v)
if err != nil {
return err
}
if i < 1 {
return fmt.Errorf("timeout must be greater than 0")
}
*t = timeoutMinutes(time.Duration(i) * time.Minute)
return nil
}
func (t timeoutMinutes) Type() string {
return "timeoutMinutes"
}