<commit_before>package cmd

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"strings"
	"time"

	"github.com/letsencrypt/boulder/core"
)

// PasswordConfig either contains a password or the path to a file
// containing a password
type PasswordConfig struct {
	Password     string
	PasswordFile string
}

// Pass returns a password, either directly from the configuration
// struct or by reading from a specified file
func (pc *PasswordConfig) Pass() (string, error) {
	if pc.PasswordFile != "" {
		contents, err := ioutil.ReadFile(pc.PasswordFile)
		if err != nil {
			return "", err
		}
		return strings.TrimRight(string(contents), "\n"), nil
	}
	return pc.Password, nil
}

// ServiceConfig contains config items that are common to all our services, to
// be embedded in other config structs.
type ServiceConfig struct {
	// DebugAddr is the address to run the /debug handlers on.
	DebugAddr string
	GRPC      *GRPCServerConfig
	TLS       TLSConfig
}

// DBConfig defines how to connect to a database. The connect string may be
// stored in a file separate from the config, because it can contain a password,
// which we want to keep out of configs.
type DBConfig struct {
	DBConnect string
	// A file containing a connect URL for the DB.
	DBConnectFile  string
	MaxDBConns     int
	MaxIdleDBConns int
}

// URL returns the DBConnect URL represented by this DBConfig object, either
// loading it from disk or returning a default value. Leading and trailing
// whitespace is stripped.
func (d *DBConfig) URL() (string, error) {
	if d.DBConnectFile != "" {
		url, err := ioutil.ReadFile(d.DBConnectFile)
		return strings.TrimSpace(string(url)), err
	}
	return d.DBConnect, nil
}

type SMTPConfig struct {
	PasswordConfig
	Server   string
	Port     string
	Username string
}

// PAConfig specifies how a policy authority should connect to its
// database, what policies it should enforce, and what challenges
// it should offer.
type PAConfig struct {
	DBConfig
	EnforcePolicyWhitelist  bool
	Challenges              map[string]bool
	ChallengesWhitelistFile string
}

// HostnamePolicyConfig specifies a file from which to load a policy regarding
// what hostnames to issue for.
type HostnamePolicyConfig struct {
	HostnamePolicyFile string
}

// CheckChallenges checks whether the list of challenges in the PA config
// actually contains valid challenge names
func (pc PAConfig) CheckChallenges() error {
	if len(pc.Challenges) == 0 {
		return errors.New("empty challenges map in the Policy Authority config is not allowed")
	}
	for name := range pc.Challenges {
		if !core.ValidChallenge(name) {
			return fmt.Errorf("Invalid challenge in PA config: %s", name)
		}
	}
	return nil
}

// TLSConfig represents certificates and a key for authenticated TLS.
type TLSConfig struct {
	CertFile   *string
	KeyFile    *string
	CACertFile *string
}

// Load reads and parses the certificates and key listed in the TLSConfig, and
// returns a *tls.Config suitable for either client or server use.
func (t *TLSConfig) Load() (*tls.Config, error) {
	if t == nil {
		return nil, fmt.Errorf("nil TLS section in config")
	}
	if t.CertFile == nil {
		return nil, fmt.Errorf("nil CertFile in TLSConfig")
	}
	if t.KeyFile == nil {
		return nil, fmt.Errorf("nil KeyFile in TLSConfig")
	}
	if t.CACertFile == nil {
		return nil, fmt.Errorf("nil CACertFile in TLSConfig")
	}
	caCertBytes, err := ioutil.ReadFile(*t.CACertFile)
	if err != nil {
		return nil, fmt.Errorf("reading CA cert from %q: %s", *t.CACertFile, err)
	}
	rootCAs := x509.NewCertPool()
	if ok := rootCAs.AppendCertsFromPEM(caCertBytes); !ok {
		return nil, fmt.Errorf("parsing CA certs from %s failed", *t.CACertFile)
	}
	cert, err := tls.LoadX509KeyPair(*t.CertFile, *t.KeyFile)
	if err != nil {
		return nil, fmt.Errorf("loading key pair from %q and %q: %s",
			*t.CertFile, *t.KeyFile, err)
	}
	return &tls.Config{
		RootCAs:      rootCAs,
		ClientCAs:    rootCAs,
		ClientAuth:   tls.RequireAndVerifyClientCert,
		Certificates: []tls.Certificate{cert},
	}, nil
}

// RPCServerConfig contains configuration particular to a specific RPC server
// type (e.g. RA, SA, etc)
type RPCServerConfig struct {
	Server     string // Queue name where the server receives requests
	RPCTimeout ConfigDuration
}

// OCSPUpdaterConfig provides the various window tick times and batch sizes needed
// for the OCSP (and SCT) updater
type OCSPUpdaterConfig struct {
	ServiceConfig
	DBConfig

	NewCertificateWindow     ConfigDuration
	OldOCSPWindow            ConfigDuration
	MissingSCTWindow         ConfigDuration
	RevokedCertificateWindow ConfigDuration

	NewCertificateBatchSize     int
	OldOCSPBatchSize            int
	MissingSCTBatchSize         int
	RevokedCertificateBatchSize int

	OCSPMinTimeToExpiry          ConfigDuration
	OCSPStaleMaxAge              ConfigDuration
	OldestIssuedSCT              ConfigDuration
	ParallelGenerateOCSPRequests int

	AkamaiBaseURL      string
	AkamaiClientToken  string
	AkamaiClientSecret string
	AkamaiAccessToken  string
	// When AkamaiV3Network is not provided, the Akamai CCU API v2 is used. When
	// AkamaiV3Network is set to "staging" or "production" the Akamai CCU API v3
	// is used.
	AkamaiV3Network         string
	AkamaiPurgeRetries      int
	AkamaiPurgeRetryBackoff ConfigDuration

	SignFailureBackoffFactor float64
	SignFailureBackoffMax    ConfigDuration

	Publisher            *GRPCClientConfig
	SAService            *GRPCClientConfig
	OCSPGeneratorService *GRPCClientConfig

	Features map[string]bool
}

// GoogleSafeBrowsingConfig is the JSON config struct for the VA's use of the
// Google Safe Browsing API.
type GoogleSafeBrowsingConfig struct {
	APIKey    string
	DataDir   string
	ServerURL string
}

// SyslogConfig defines the config for syslogging.
type SyslogConfig struct {
	StdoutLevel int
	SyslogLevel int
}

// StatsdConfig defines the config for Statsd.
type StatsdConfig struct {
	Server string
	Prefix string
}

// ConfigDuration is just an alias for time.Duration that allows
// serialization to YAML as well as JSON.
type ConfigDuration struct {
	time.Duration
}

// ErrDurationMustBeString is returned when a non-string value is
// presented to be deserialized as a ConfigDuration
var ErrDurationMustBeString = errors.New("cannot JSON unmarshal something other than a string into a ConfigDuration")

// UnmarshalJSON parses a string into a ConfigDuration using
// time.ParseDuration.  If the input does not unmarshal as a
// string, then UnmarshalJSON returns ErrDurationMustBeString.
func (d *ConfigDuration) UnmarshalJSON(b []byte) error {
	s := ""
	err := json.Unmarshal(b, &s)
	if err != nil {
		if _, ok := err.(*json.UnmarshalTypeError); ok {
			return ErrDurationMustBeString
		}
		return err
	}
	dd, err := time.ParseDuration(s)
	d.Duration = dd
	return err
}

// MarshalJSON returns the string form of the duration, as a byte array.
func (d ConfigDuration) MarshalJSON() ([]byte, error) {
	return []byte(d.Duration.String()), nil
}

// UnmarshalYAML uses the same frmat as JSON, but is called by the YAML
// parser (vs. the JSON parser).
func (d *ConfigDuration) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	if err := unmarshal(&s); err != nil {
		return err
	}
	dur, err := time.ParseDuration(s)
	if err != nil {
		return err
	}

	d.Duration = dur
	return nil
}

// GRPCClientConfig contains the information needed to talk to the gRPC service
type GRPCClientConfig struct {
	// NOTE: this field is deprecated in favor of ServerAddress, as we only ever
	// expect a single address
	ServerAddresses []string
	ServerAddress   string
	Timeout         ConfigDuration
}

// GRPCServerConfig contains the information needed to run a gRPC service
type GRPCServerConfig struct {
	Address string `json:"address"`
	// ClientNames is a list of allowed client certificate subject alternate names
	// (SANs). The server will reject clients that do not present a certificate
	// with a SAN present on the `ClientNames` list.
	ClientNames []string `json:"clientNames"`
	// gRPC multiplexes RPCs across HTTP/2 streams in a single TCP connection.
	// HTTP/2 servers are allowed to set a limit on the number of streams a client
	// will create. In Go by default that limit is 250. We can override that for
	// our servers with this config value. In practice this is a limit on how many
	// concurrent requests we can handle.
	MaxConcurrentStreams int
}

// PortConfig specifies what ports the VA should call to on the remote
// host when performing its checks.
type PortConfig struct {
	HTTPPort  int
	HTTPSPort int
	TLSPort   int
}

// CAADistributedResolverConfig specifies the HTTP client setup and interfaces
// needed to resolve CAA addresses over multiple paths
type CAADistributedResolverConfig struct {
	Timeout     ConfigDuration
	MaxFailures int
	Proxies     []string
}

// LogShard describes a single shard of a temporally sharded
// CT log
type LogShard struct {
	URI         string
	Key         string
	WindowStart time.Time
	WindowEnd   time.Time
}

// TemporalSet contains a set of temporal shards of a single log
type TemporalSet struct {
	Name   string
	Shards []LogShard
}

// Setup initializes the TemporalSet by parsing the start and end dates
// and verifying WindowEnd > WindowStart
func (ts *TemporalSet) Setup() error {
	if ts.Name == "" {
		return errors.New("Name cannot be empty")
	}
	if len(ts.Shards) == 0 {
		return errors.New("temporal set contains no shards")
	}
	for i := range ts.Shards {
		if ts.Shards[i].WindowEnd.Before(ts.Shards[i].WindowStart) ||
			ts.Shards[i].WindowEnd.Equal(ts.Shards[i].WindowStart) {
			return errors.New("WindowStart must be before WindowEnd")
		}
	}
	return nil
}

// pick chooses the correct shard from a TemporalSet to use for the given
// expiration time. In the case where two shards have overlapping windows
// the earlier of the two shards will be chosen.
func (ts *TemporalSet) pick(exp time.Time) (*LogShard, error) {
	for _, shard := range ts.Shards {
		if exp.Before(shard.WindowStart) {
			continue
		}
		if !exp.Before(shard.WindowEnd) {
			continue
		}
		return &shard, nil
	}
	return nil, fmt.Errorf("no valid shard available for temporal set %q for expiration date %q", ts.Name, exp)
}

// LogDescription contains the information needed to submit certificates
// to a CT log and verify returned receipts. If TemporalSet is non-nil then
// URI and Key should be empty.
type LogDescription struct {
	URI             string
	Key             string
	SubmitFinalCert bool

	*TemporalSet
}

// Info returns the URI and key of the log, either from a plain log description
// or from the earliest valid shard from a temporal log set
func (ld LogDescription) Info(exp time.Time) (string, string, error) {
	if ld.TemporalSet == nil {
		return ld.URI, ld.Key, nil
	}
	shard, err := ld.TemporalSet.pick(exp)
	if err != nil {
		return "", "", err
	}
	return shard.URI, shard.Key, nil
}

type CTGroup struct {
	Name string
	Logs []LogDescription
	// How long to wait for one log to accept a certificate before moving on to
	// the next.
	Stagger ConfigDuration
}
<commit_msg>Remove unused OCSP Updater Publisher config (#3978)<commit_after>package cmd

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"strings"
	"time"

	"github.com/letsencrypt/boulder/core"
)

// PasswordConfig either contains a password or the path to a file
// containing a password
type PasswordConfig struct {
	Password     string
	PasswordFile string
}

// Pass returns a password, either directly from the configuration
// struct or by reading from a specified file
func (pc *PasswordConfig) Pass() (string, error) {
	if pc.PasswordFile != "" {
		contents, err := ioutil.ReadFile(pc.PasswordFile)
		if err != nil {
			return "", err
		}
		return strings.TrimRight(string(contents), "\n"), nil
	}
	return pc.Password, nil
}

// ServiceConfig contains config items that are common to all our services, to
// be embedded in other config structs.
type ServiceConfig struct {
	// DebugAddr is the address to run the /debug handlers on.
	DebugAddr string
	GRPC      *GRPCServerConfig
	TLS       TLSConfig
}

// DBConfig defines how to connect to a database. The connect string may be
// stored in a file separate from the config, because it can contain a password,
// which we want to keep out of configs.
type DBConfig struct {
	DBConnect string
	// A file containing a connect URL for the DB.
	DBConnectFile  string
	MaxDBConns     int
	MaxIdleDBConns int
}

// URL returns the DBConnect URL represented by this DBConfig object, either
// loading it from disk or returning a default value. Leading and trailing
// whitespace is stripped.
func (d *DBConfig) URL() (string, error) {
	if d.DBConnectFile != "" {
		url, err := ioutil.ReadFile(d.DBConnectFile)
		return strings.TrimSpace(string(url)), err
	}
	return d.DBConnect, nil
}

type SMTPConfig struct {
	PasswordConfig
	Server   string
	Port     string
	Username string
}

// PAConfig specifies how a policy authority should connect to its
// database, what policies it should enforce, and what challenges
// it should offer.
type PAConfig struct {
	DBConfig
	EnforcePolicyWhitelist  bool
	Challenges              map[string]bool
	ChallengesWhitelistFile string
}

// HostnamePolicyConfig specifies a file from which to load a policy regarding
// what hostnames to issue for.
type HostnamePolicyConfig struct {
	HostnamePolicyFile string
}

// CheckChallenges checks whether the list of challenges in the PA config
// actually contains valid challenge names
func (pc PAConfig) CheckChallenges() error {
	if len(pc.Challenges) == 0 {
		return errors.New("empty challenges map in the Policy Authority config is not allowed")
	}
	for name := range pc.Challenges {
		if !core.ValidChallenge(name) {
			return fmt.Errorf("Invalid challenge in PA config: %s", name)
		}
	}
	return nil
}

// TLSConfig represents certificates and a key for authenticated TLS.
type TLSConfig struct {
	CertFile   *string
	KeyFile    *string
	CACertFile *string
}

// Load reads and parses the certificates and key listed in the TLSConfig, and
// returns a *tls.Config suitable for either client or server use.
func (t *TLSConfig) Load() (*tls.Config, error) {
	if t == nil {
		return nil, fmt.Errorf("nil TLS section in config")
	}
	if t.CertFile == nil {
		return nil, fmt.Errorf("nil CertFile in TLSConfig")
	}
	if t.KeyFile == nil {
		return nil, fmt.Errorf("nil KeyFile in TLSConfig")
	}
	if t.CACertFile == nil {
		return nil, fmt.Errorf("nil CACertFile in TLSConfig")
	}
	caCertBytes, err := ioutil.ReadFile(*t.CACertFile)
	if err != nil {
		return nil, fmt.Errorf("reading CA cert from %q: %s", *t.CACertFile, err)
	}
	rootCAs := x509.NewCertPool()
	if ok := rootCAs.AppendCertsFromPEM(caCertBytes); !ok {
		return nil, fmt.Errorf("parsing CA certs from %s failed", *t.CACertFile)
	}
	cert, err := tls.LoadX509KeyPair(*t.CertFile, *t.KeyFile)
	if err != nil {
		return nil, fmt.Errorf("loading key pair from %q and %q: %s",
			*t.CertFile, *t.KeyFile, err)
	}
	return &tls.Config{
		RootCAs:      rootCAs,
		ClientCAs:    rootCAs,
		ClientAuth:   tls.RequireAndVerifyClientCert,
		Certificates: []tls.Certificate{cert},
	}, nil
}

// RPCServerConfig contains configuration particular to a specific RPC server
// type (e.g. RA, SA, etc)
type RPCServerConfig struct {
	Server     string // Queue name where the server receives requests
	RPCTimeout ConfigDuration
}

// OCSPUpdaterConfig provides the various window tick times and batch sizes needed
// for the OCSP (and SCT) updater
type OCSPUpdaterConfig struct {
	ServiceConfig
	DBConfig

	NewCertificateWindow     ConfigDuration
	OldOCSPWindow            ConfigDuration
	MissingSCTWindow         ConfigDuration
	RevokedCertificateWindow ConfigDuration

	NewCertificateBatchSize     int
	OldOCSPBatchSize            int
	MissingSCTBatchSize         int
	RevokedCertificateBatchSize int

	OCSPMinTimeToExpiry          ConfigDuration
	OCSPStaleMaxAge              ConfigDuration
	OldestIssuedSCT              ConfigDuration
	ParallelGenerateOCSPRequests int

	AkamaiBaseURL      string
	AkamaiClientToken  string
	AkamaiClientSecret string
	AkamaiAccessToken  string
	// When AkamaiV3Network is not provided, the Akamai CCU API v2 is used. When
	// AkamaiV3Network is set to "staging" or "production" the Akamai CCU API v3
	// is used.
	AkamaiV3Network         string
	AkamaiPurgeRetries      int
	AkamaiPurgeRetryBackoff ConfigDuration

	SignFailureBackoffFactor float64
	SignFailureBackoffMax    ConfigDuration

	SAService            *GRPCClientConfig
	OCSPGeneratorService *GRPCClientConfig

	Features map[string]bool
}

// GoogleSafeBrowsingConfig is the JSON config struct for the VA's use of the
// Google Safe Browsing API.
type GoogleSafeBrowsingConfig struct {
	APIKey    string
	DataDir   string
	ServerURL string
}

// SyslogConfig defines the config for syslogging.
type SyslogConfig struct {
	StdoutLevel int
	SyslogLevel int
}

// StatsdConfig defines the config for Statsd.
type StatsdConfig struct {
	Server string
	Prefix string
}

// ConfigDuration is just an alias for time.Duration that allows
// serialization to YAML as well as JSON.
type ConfigDuration struct {
	time.Duration
}

// ErrDurationMustBeString is returned when a non-string value is
// presented to be deserialized as a ConfigDuration
var ErrDurationMustBeString = errors.New("cannot JSON unmarshal something other than a string into a ConfigDuration")

// UnmarshalJSON parses a string into a ConfigDuration using
// time.ParseDuration.  If the input does not unmarshal as a
// string, then UnmarshalJSON returns ErrDurationMustBeString.
func (d *ConfigDuration) UnmarshalJSON(b []byte) error {
	s := ""
	err := json.Unmarshal(b, &s)
	if err != nil {
		if _, ok := err.(*json.UnmarshalTypeError); ok {
			return ErrDurationMustBeString
		}
		return err
	}
	dd, err := time.ParseDuration(s)
	d.Duration = dd
	return err
}

// MarshalJSON returns the string form of the duration, as a byte array.
func (d ConfigDuration) MarshalJSON() ([]byte, error) {
	return []byte(d.Duration.String()), nil
}

// UnmarshalYAML uses the same frmat as JSON, but is called by the YAML
// parser (vs. the JSON parser).
func (d *ConfigDuration) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	if err := unmarshal(&s); err != nil {
		return err
	}
	dur, err := time.ParseDuration(s)
	if err != nil {
		return err
	}

	d.Duration = dur
	return nil
}

// GRPCClientConfig contains the information needed to talk to the gRPC service
type GRPCClientConfig struct {
	// NOTE: this field is deprecated in favor of ServerAddress, as we only ever
	// expect a single address
	ServerAddresses []string
	ServerAddress   string
	Timeout         ConfigDuration
}

// GRPCServerConfig contains the information needed to run a gRPC service
type GRPCServerConfig struct {
	Address string `json:"address"`
	// ClientNames is a list of allowed client certificate subject alternate names
	// (SANs). The server will reject clients that do not present a certificate
	// with a SAN present on the `ClientNames` list.
	ClientNames []string `json:"clientNames"`
	// gRPC multiplexes RPCs across HTTP/2 streams in a single TCP connection.
	// HTTP/2 servers are allowed to set a limit on the number of streams a client
	// will create. In Go by default that limit is 250. We can override that for
	// our servers with this config value. In practice this is a limit on how many
	// concurrent requests we can handle.
	MaxConcurrentStreams int
}

// PortConfig specifies what ports the VA should call to on the remote
// host when performing its checks.
type PortConfig struct {
	HTTPPort  int
	HTTPSPort int
	TLSPort   int
}

// CAADistributedResolverConfig specifies the HTTP client setup and interfaces
// needed to resolve CAA addresses over multiple paths
type CAADistributedResolverConfig struct {
	Timeout     ConfigDuration
	MaxFailures int
	Proxies     []string
}

// LogShard describes a single shard of a temporally sharded
// CT log
type LogShard struct {
	URI         string
	Key         string
	WindowStart time.Time
	WindowEnd   time.Time
}

// TemporalSet contains a set of temporal shards of a single log
type TemporalSet struct {
	Name   string
	Shards []LogShard
}

// Setup initializes the TemporalSet by parsing the start and end dates
// and verifying WindowEnd > WindowStart
func (ts *TemporalSet) Setup() error {
	if ts.Name == "" {
		return errors.New("Name cannot be empty")
	}
	if len(ts.Shards) == 0 {
		return errors.New("temporal set contains no shards")
	}
	for i := range ts.Shards {
		if ts.Shards[i].WindowEnd.Before(ts.Shards[i].WindowStart) ||
			ts.Shards[i].WindowEnd.Equal(ts.Shards[i].WindowStart) {
			return errors.New("WindowStart must be before WindowEnd")
		}
	}
	return nil
}

// pick chooses the correct shard from a TemporalSet to use for the given
// expiration time. In the case where two shards have overlapping windows
// the earlier of the two shards will be chosen.
func (ts *TemporalSet) pick(exp time.Time) (*LogShard, error) {
	for _, shard := range ts.Shards {
		if exp.Before(shard.WindowStart) {
			continue
		}
		if !exp.Before(shard.WindowEnd) {
			continue
		}
		return &shard, nil
	}
	return nil, fmt.Errorf("no valid shard available for temporal set %q for expiration date %q", ts.Name, exp)
}

// LogDescription contains the information needed to submit certificates
// to a CT log and verify returned receipts. If TemporalSet is non-nil then
// URI and Key should be empty.
type LogDescription struct {
	URI             string
	Key             string
	SubmitFinalCert bool

	*TemporalSet
}

// Info returns the URI and key of the log, either from a plain log description
// or from the earliest valid shard from a temporal log set
func (ld LogDescription) Info(exp time.Time) (string, string, error) {
	if ld.TemporalSet == nil {
		return ld.URI, ld.Key, nil
	}
	shard, err := ld.TemporalSet.pick(exp)
	if err != nil {
		return "", "", err
	}
	return shard.URI, shard.Key, nil
}

type CTGroup struct {
	Name string
	Logs []LogDescription
	// How long to wait for one log to accept a certificate before moving on to
	// the next.
	Stagger ConfigDuration
}
<|endoftext|>


<commit_before>/*
Copyright © 2020 NAME HERE <EMAIL ADDRESS>

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 cmd

import (
	"fmt"
	"os"
	"sort"

	"github.com/ondevice/ondevice/cmd/internal"
	"github.com/ondevice/ondevice/config"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

func configRun(cmd *cobra.Command, args []string) {
	var values = config.MustLoad().AllValues()

	var keys []string
	for k := range values {
		keys = append(keys, k)
	}

	sort.Strings(keys)
	for _, key := range keys {
		fmt.Printf("%s=%s\n", key, values[key])
	}
}

// configCmd represents the config command
var configCmd = &cobra.Command{
	Use:   "config",
	Short: "show or modify your local ondevice configuration",
	Long:  `calling ondevice config without parameters will print a list of key=value lines`,
	Run:   configRun,
	//Hidden: true,
}

var configGetCmd = &cobra.Command{
	Use:   "get <key>",
	Short: "print individual configuration values",
	Long: `ondevice config get prints one or more configuration values.

when more than one key is specified, each line of output will contain 'key=value' pairs.
when only one key is requested, only the value will be printed`,
	Example: `  $ ondevice config get ssh.path
  ssh

  $ ondevice config get ssh.path rsync.path
  ssh.path=ssh
  rsync.path=rsync`,
	Run: func(cmd *cobra.Command, args []string) {
		var printKeyVal = len(args) > 1 // print in the form key=val if more than one key was specified
		var rc = 0

		var cfg = config.MustLoad()

		for _, keyName := range args {
			var key = config.FindKey(keyName)
			if key == nil {
				logrus.Errorf("config key not found: %v", key)
				rc = 1
				continue
			}

			var val = cfg.GetString(*key)

			if printKeyVal {
				fmt.Printf("%v=%s\n", key, val)
			} else {
				fmt.Println(val)
			}
		}

		if rc != 0 {
			os.Exit(rc)
		}
	},
	Args:              cobra.MinimumNArgs(1),
	ValidArgsFunction: internal.ConfigCompletion{WithReadOnly: true}.Run,
}

func init() {
	rootCmd.AddCommand(configCmd)
	configCmd.AddCommand(configGetCmd)
	//	configCmd.AddCommand(configSetCmd)
	//	configCmd.AddCommand(configUnsetCmd)
}
<commit_msg>added `ondevice config set` command<commit_after>/*
Copyright © 2020 NAME HERE <EMAIL ADDRESS>

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 cmd

import (
	"fmt"
	"os"
	"sort"
	"strings"

	"github.com/ondevice/ondevice/cmd/internal"
	"github.com/ondevice/ondevice/config"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

func configRun(cmd *cobra.Command, args []string) {
	var values = config.MustLoad().AllValues()

	var keys []string
	for k := range values {
		keys = append(keys, k)
	}

	sort.Strings(keys)
	for _, key := range keys {
		fmt.Printf("%s=%s\n", key, values[key])
	}
}

// configCmd represents the config command
var configCmd = &cobra.Command{
	Use:   "config",
	Short: "show or modify your local ondevice configuration",
	Long:  `calling ondevice config without parameters will print a list of key=value lines`,
	Run:   configRun,
	//Hidden: true,
}

var configGetCmd = &cobra.Command{
	Use:   "get <key>",
	Short: "print individual configuration values",
	Long: `ondevice config get prints one or more configuration values.

when more than one key is specified, each line of output will contain 'key=value' pairs.
when only one key is requested, only the value will be printed`,
	Example: `  $ ondevice config get ssh.path
  ssh

  $ ondevice config get ssh.path rsync.path
  ssh.path=ssh
  rsync.path=rsync`,
	Run: func(cmd *cobra.Command, args []string) {
		var printKeyVal = len(args) > 1 // print in the form key=val if more than one key was specified
		var rc = 0

		var cfg = config.MustLoad()

		for _, keyName := range args {
			var key = config.FindKey(keyName)
			if key == nil {
				logrus.Errorf("config key not found: %v", key)
				rc = 1
				continue
			}

			var val = cfg.GetString(*key)

			if printKeyVal {
				fmt.Printf("%v=%s\n", key, val)
			} else {
				fmt.Println(val)
			}
		}

		if rc != 0 {
			os.Exit(rc)
		}
	},
	Args:              cobra.MinimumNArgs(1),
	ValidArgsFunction: internal.ConfigCompletion{WithReadOnly: true}.Run,
}

var configSetCmd = &cobra.Command{
	Use:   "set <key>=<value>...",
	Short: "set one or more configuration values",
	Long: `ondevice config set updates one or more configuration values.
If you specify the same key more than once, the last one in the list wins`,
	Example: `  $ ondevice config set ssh.path=/usr/local/bin/ssh rsync.path=echo
  $ ondevice config set client.timeout=5
`,
	Run: func(cmd *cobra.Command, args []string) {
		var rc = 0

		var cfg = config.MustLoad()
		for _, keyValue := range args {
			var parts = strings.SplitN(keyValue, "=", 2)
			if len(parts) != 2 {
				logrus.Fatalf("malformed argument, expected key=value pairs: '%s'", keyValue)
			}
			var key = config.FindKey(parts[0])
			var newValue = parts[1]

			if key == nil {
				logrus.Fatalf("config key not found: %v", key)
				rc = 1
				continue
			}

			// TODO run validation
			if err := cfg.SetValue(*key, newValue); err != nil {
				logrus.WithError(err).Error("failed to set '%v'", key)
				rc = 1
			}
		}

		if rc != 0 {
			os.Exit(rc)
		}

		if err := cfg.Write(); err != nil {
			logrus.WithError(err).Error("failed to write config")
		}
	},
	Args:              cobra.MinimumNArgs(1),
	ValidArgsFunction: internal.ConfigCompletion{WithReadOnly: false, Suffix: "="}.Run,
}

func init() {
	rootCmd.AddCommand(configCmd)
	configCmd.AddCommand(configGetCmd)
	configCmd.AddCommand(configSetCmd)
	//	configCmd.AddCommand(configUnsetCmd)
}
<|endoftext|>


<commit_before>package queue

import (
	"flag"
	"testing"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	ojson "github.com/Cepave/open-falcon-backend/common/json"
	commonQueue "github.com/Cepave/open-falcon-backend/common/queue"
	dbTest "github.com/Cepave/open-falcon-backend/common/testing/db"
	"github.com/Cepave/open-falcon-backend/modules/nqm-mng/model"
	"github.com/Cepave/open-falcon-backend/modules/nqm-mng/rdb"
)

func init() {
	flag.Parse()
}

func TestByGinkgo(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Base Suite")
}

var _ = Describe("Start(): Start the queue service", ginkgoDb.NeedDb(func() {
	It("can't be put elements without calling Start() in advance", func() {
		testedQueue := New(&commonQueue.Config{Num: 1, Dur: 0})

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test1-hostname@1.2.3.4",
			Hostname:     "test1-hostname",
			IpAddress:    "1.2.3.4",
			Timestamp:    ojson.JsonTime(time.Now()),
		})
		Expect(testedQueue.Count()).To(Equal(uint64(0)))
		Expect(testedQueue.Len()).To(Equal(0))
	})

	It("can be put elements by calling Start() in advance", func() {
		testedQueue := New(&commonQueue.Config{Num: 1, Dur: 0})

		testedQueue.Start()

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test1-hostname@1.2.3.4",
			Hostname:     "test1-hostname",
			IpAddress:    "1.2.3.4",
			Timestamp:    ojson.JsonTime(time.Now()),
		})

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test2-hostname@1.2.3.4",
			Hostname:     "test2-hostname",
			IpAddress:    "1.2.3.4",
			Timestamp:    ojson.JsonTime(time.Now()),
		})

		testedQueue.Stop()
		Expect(testedQueue.Count()).To(Equal(uint64(2)))
	})
}))

var _ = Describe("Stop(): Stop the queue service", ginkgoDb.NeedDb(func() {
	It("can't be put elements after being stopped", func() {
		testedQueue := New(&commonQueue.Config{Num: 1, Dur: 0})

		testedQueue.Start()
		testedQueue.Stop()

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test1-hostname@1.2.3.4",
			Hostname:     "test1-hostname",
			IpAddress:    "1.2.3.4",
			Timestamp:    ojson.JsonTime(time.Now()),
		})
		Expect(testedQueue.Count()).To(Equal(uint64(0)))
		Expect(testedQueue.Len()).To(Equal(0))
	})

	It("doesn't flush elements until Stop() is called", func() {
		testedQueue := New(&commonQueue.Config{Num: 10, Dur: 1 * time.Second})

		testedQueue.Start()

		for i := 0; i < 9; i++ {
			testedQueue.Put(&model.NqmAgentHeartbeatRequest{
				ConnectionId: "test1-hostname@1.2.3.4",
				Hostname:     "test1-hostname",
				IpAddress:    "1.2.3.4",
				Timestamp:    ojson.JsonTime(time.Now()),
			})
		}
		Ω(testedQueue.Count()).Should(BeNumerically("<", uint64(9)))
		Ω(testedQueue.Len()).Should(BeNumerically(">", 0))
		testedQueue.Stop()
	})

	It("has no elements after Stop() is called", func() {
		testedQueue := New(&commonQueue.Config{Num: 10, Dur: 1 * time.Second})

		testedQueue.Start()

		go func() {
			t := time.After(500 * time.Millisecond)
			for {
				select {
				default:
					time.Sleep(100 * time.Millisecond)
					testedQueue.Put(&model.NqmAgentHeartbeatRequest{
						ConnectionId: "test1-hostname@1.2.3.4",
						Hostname:     "test1-hostname",
						IpAddress:    "1.2.3.4",
						Timestamp:    ojson.JsonTime(time.Now()),
					})
				case <-t:
					return
				}
			}
		}()
		time.Sleep(200 * time.Millisecond)
		testedQueue.Stop()
		Expect(testedQueue.Len()).To(Equal(0))
	})
}))

var ginkgoDb = &dbTest.GinkgoDb{}

var _ = BeforeSuite(func() {
	rdb.DbFacade = ginkgoDb.InitDbFacade()
})

var _ = AfterSuite(func() {
	ginkgoDb.ReleaseDbFacade(rdb.DbFacade)
})
<commit_msg>[OWL-1730][nqm-mng] Fix tests in `service/queue/`<commit_after>package queue

import (
	"flag"
	"testing"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	ojson "github.com/Cepave/open-falcon-backend/common/json"
	commonQueue "github.com/Cepave/open-falcon-backend/common/queue"
	dbTest "github.com/Cepave/open-falcon-backend/common/testing/db"
	"github.com/Cepave/open-falcon-backend/modules/nqm-mng/model"
	"github.com/Cepave/open-falcon-backend/modules/nqm-mng/rdb"
)

func init() {
	flag.Parse()
}

func TestByGinkgo(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Base Suite")
}

var _ = Describe("Start(): Start the queue service", ginkgoDb.NeedDb(func() {
	It("can't be put elements without calling Start() in advance", func() {
		testedQueue := New(&commonQueue.Config{Num: 1, Dur: 0})

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test1-hostname@1.2.3.4",
			Hostname:     "test1-hostname",
			IpAddress:    ojson.NewIP("1.2.3.4"),
			Timestamp:    ojson.JsonTime(time.Now()),
		})
		Expect(testedQueue.Count()).To(Equal(uint64(0)))
		Expect(testedQueue.Len()).To(Equal(0))
	})

	It("can be put elements by calling Start() in advance", func() {
		testedQueue := New(&commonQueue.Config{Num: 1, Dur: 0})

		testedQueue.Start()

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test1-hostname@1.2.3.4",
			Hostname:     "test1-hostname",
			IpAddress:    ojson.NewIP("1.2.3.4"),
			Timestamp:    ojson.JsonTime(time.Now()),
		})

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test2-hostname@1.2.3.4",
			Hostname:     "test2-hostname",
			IpAddress:    ojson.NewIP("1.2.3.4"),
			Timestamp:    ojson.JsonTime(time.Now()),
		})

		testedQueue.Stop()
		Expect(testedQueue.Count()).To(Equal(uint64(2)))
	})
}))

var _ = Describe("Stop(): Stop the queue service", ginkgoDb.NeedDb(func() {
	It("can't be put elements after being stopped", func() {
		testedQueue := New(&commonQueue.Config{Num: 1, Dur: 0})

		testedQueue.Start()
		testedQueue.Stop()

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test1-hostname@1.2.3.4",
			Hostname:     "test1-hostname",
			IpAddress:    ojson.NewIP("1.2.3.4"),
			Timestamp:    ojson.JsonTime(time.Now()),
		})
		Expect(testedQueue.Count()).To(Equal(uint64(0)))
		Expect(testedQueue.Len()).To(Equal(0))
	})

	It("doesn't flush elements until Stop() is called", func() {
		testedQueue := New(&commonQueue.Config{Num: 10, Dur: 1 * time.Second})

		testedQueue.Start()

		for i := 0; i < 9; i++ {
			testedQueue.Put(&model.NqmAgentHeartbeatRequest{
				ConnectionId: "test1-hostname@1.2.3.4",
				Hostname:     "test1-hostname",
				IpAddress:    ojson.NewIP("1.2.3.4"),
				Timestamp:    ojson.JsonTime(time.Now()),
			})
		}
		Ω(testedQueue.Count()).Should(BeNumerically("<", uint64(9)))
		Ω(testedQueue.Len()).Should(BeNumerically(">", 0))
		testedQueue.Stop()
	})

	It("has no elements after Stop() is called", func() {
		testedQueue := New(&commonQueue.Config{Num: 10, Dur: 1 * time.Second})

		testedQueue.Start()

		go func() {
			t := time.After(500 * time.Millisecond)
			for {
				select {
				default:
					time.Sleep(100 * time.Millisecond)
					testedQueue.Put(&model.NqmAgentHeartbeatRequest{
						ConnectionId: "test1-hostname@1.2.3.4",
						Hostname:     "test1-hostname",
						IpAddress:    ojson.NewIP("1.2.3.4"),
						Timestamp:    ojson.JsonTime(time.Now()),
					})
				case <-t:
					return
				}
			}
		}()
		time.Sleep(200 * time.Millisecond)
		testedQueue.Stop()
		Expect(testedQueue.Len()).To(Equal(0))
	})
}))

var ginkgoDb = &dbTest.GinkgoDb{}

var _ = BeforeSuite(func() {
	rdb.DbFacade = ginkgoDb.InitDbFacade()
})

var _ = AfterSuite(func() {
	ginkgoDb.ReleaseDbFacade(rdb.DbFacade)
})
<|endoftext|>


<commit_before><commit_msg>Add force_install to spt<commit_after><|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/opencontainers/runtime-tools/validation/util"
)

func checkReadonlyPaths() error {
	g, err := util.GetDefaultGenerator()
	if err != nil {
		return err
	}

	readonlyDir := "readonly-dir"
	readonlySubDir := "readonly-subdir"
	readonlyFile := "readonly-file"

	readonlyDirTop := filepath.Join("/", readonlyDir)
	readonlyFileTop := filepath.Join("/", readonlyFile)

	readonlyDirSub := filepath.Join(readonlyDirTop, readonlySubDir)
	readonlyFileSub := filepath.Join(readonlyDirTop, readonlyFile)
	readonlyFileSubSub := filepath.Join(readonlyDirSub, readonlyFile)

	g.AddLinuxReadonlyPaths(readonlyDirTop)
	g.AddLinuxReadonlyPaths(readonlyFileTop)
	g.AddLinuxReadonlyPaths(readonlyDirSub)
	g.AddLinuxReadonlyPaths(readonlyFileSub)
	g.AddLinuxReadonlyPaths(readonlyFileSubSub)
	err = util.RuntimeInsideValidate(g, func(path string) error {
		testDir := filepath.Join(path, readonlyDirSub)
		err = os.MkdirAll(testDir, 0777)
		if err != nil {
			return err
		}
		// create a temp file to make testDir non-empty
		tmpfile, err := ioutil.TempFile(testDir, "tmp")
		if err != nil {
			return err
		}
		defer os.Remove(tmpfile.Name())

		// runtimetest cannot check the readability of empty files, so
		// write something.
		testSubSubFile := filepath.Join(path, readonlyFileSubSub)
		if err := ioutil.WriteFile(testSubSubFile, []byte("immutable"), 0777); err != nil {
			return err
		}

		testSubFile := filepath.Join(path, readonlyFileSub)
		if err := ioutil.WriteFile(testSubFile, []byte("immutable"), 0777); err != nil {
			return err
		}

		testFile := filepath.Join(path, readonlyFile)
		return ioutil.WriteFile(testFile, []byte("immutable"), 0777)
	})
	return err
}

func main() {
	if err := checkReadonlyPaths(); err != nil {
		util.Fatal(err)
	}

}
<commit_msg>validation: check for a read-only relative path<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/opencontainers/runtime-tools/validation/util"
)

func checkReadonlyPaths() error {
	g, err := util.GetDefaultGenerator()
	if err != nil {
		return err
	}

	readonlyDir := "readonly-dir"
	readonlySubDir := "readonly-subdir"
	readonlyFile := "readonly-file"

	readonlyDirTop := filepath.Join("/", readonlyDir)
	readonlyFileTop := filepath.Join("/", readonlyFile)

	readonlyDirSub := filepath.Join(readonlyDirTop, readonlySubDir)
	readonlyFileSub := filepath.Join(readonlyDirTop, readonlyFile)
	readonlyFileSubSub := filepath.Join(readonlyDirSub, readonlyFile)

	g.AddLinuxReadonlyPaths(readonlyDirTop)
	g.AddLinuxReadonlyPaths(readonlyFileTop)
	g.AddLinuxReadonlyPaths(readonlyDirSub)
	g.AddLinuxReadonlyPaths(readonlyFileSub)
	g.AddLinuxReadonlyPaths(readonlyFileSubSub)
	err = util.RuntimeInsideValidate(g, func(path string) error {
		testDir := filepath.Join(path, readonlyDirSub)
		err = os.MkdirAll(testDir, 0777)
		if err != nil {
			return err
		}
		// create a temp file to make testDir non-empty
		tmpfile, err := ioutil.TempFile(testDir, "tmp")
		if err != nil {
			return err
		}
		defer os.Remove(tmpfile.Name())

		// runtimetest cannot check the readability of empty files, so
		// write something.
		testSubSubFile := filepath.Join(path, readonlyFileSubSub)
		if err := ioutil.WriteFile(testSubSubFile, []byte("immutable"), 0777); err != nil {
			return err
		}

		testSubFile := filepath.Join(path, readonlyFileSub)
		if err := ioutil.WriteFile(testSubFile, []byte("immutable"), 0777); err != nil {
			return err
		}

		testFile := filepath.Join(path, readonlyFile)
		return ioutil.WriteFile(testFile, []byte("immutable"), 0777)
	})
	return err
}

func checkReadonlyRelPaths() error {
	g, err := util.GetDefaultGenerator()
	if err != nil {
		return err
	}

	// Deliberately set a relative path to be read-only, and expect an error
	readonlyRelPath := "readonly-relpath"

	g.AddLinuxReadonlyPaths(readonlyRelPath)
	err = util.RuntimeInsideValidate(g, func(path string) error {
		testFile := filepath.Join(path, readonlyRelPath)
		if _, err := os.Stat(testFile); err != nil && os.IsNotExist(err) {
			return err
		}

		return nil
	})
	if err != nil {
		return nil
	}
	return fmt.Errorf("expected: err != nil, actual: err == nil")
}

func main() {
	if err := checkReadonlyPaths(); err != nil {
		util.Fatal(err)
	}

	if err := checkReadonlyRelPaths(); err != nil {
		util.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package v2

import (
	"fmt"
	"math"
	"sort"
	"strings"

	"code.cloudfoundry.org/cli/actors/v2actions"
	"code.cloudfoundry.org/cli/cf"
	"code.cloudfoundry.org/cli/commands"
	"code.cloudfoundry.org/cli/commands/flags"
	"code.cloudfoundry.org/cli/commands/v2/internal"
	"code.cloudfoundry.org/cli/utils/config"
	"code.cloudfoundry.org/cli/utils/sortutils"
)

//go:generate counterfeiter . HelpActor

// HelpActor handles the business logic of the help command
type HelpActor interface {
	// CommandInfoByName returns back a help command information for the given
	// command
	CommandInfoByName(interface{}, string) (v2actions.CommandInfo, error)

	// CommandInfos returns a list of all commands
	CommandInfos(interface{}) map[string]v2actions.CommandInfo
}

type HelpCommand struct {
	UI     commands.UI
	Actor  HelpActor
	Config commands.Config

	OptionalArgs flags.CommandName `positional-args:"yes"`
	AllCommands  bool              `short:"a" description:"All available CLI commands"`
	usage        interface{}       `usage:"CF_NAME help [COMMAND]"`
}

func (cmd *HelpCommand) Setup(config commands.Config, ui commands.UI) error {
	cmd.Actor = v2actions.NewActor()
	cmd.Config = config
	cmd.UI = ui

	return nil
}

func (cmd HelpCommand) Execute(args []string) error {
	var err error
	if cmd.OptionalArgs.CommandName == "" {
		cmd.displayFullHelp()
	} else {
		err = cmd.displayCommand()
	}

	return err
}

func (cmd HelpCommand) displayFullHelp() {
	cmd.displayHelpPreamble()
	if cmd.AllCommands {
		cmd.displayAllCommands()
		cmd.displayHelpFooter()
	} else {
		cmd.displayCommonCommands()
	}
}

func (cmd HelpCommand) displayHelpPreamble() {
	cmd.UI.DisplayHelpHeader("NAME:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} - {{.CommandDescription}}",
		[]string{"CommandDescription"},
		map[string]interface{}{
			"CommandName":        cmd.Config.BinaryName(),
			"CommandDescription": "A command line tool to interact with Cloud Foundry",
		})
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("USAGE:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} {{.CommandUsage}}",
		[]string{"CommandUsage"},
		map[string]interface{}{
			"CommandName":  cmd.Config.BinaryName(),
			"CommandUsage": "[global options] command [arguments...] [command options]",
		})
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("VERSION:")
	cmd.UI.DisplayText("   {{.Version}}-{{.Time}}", map[string]interface{}{
		"Version": cf.Version,
		"Time":    cf.BuiltOnDate,
	})
	cmd.UI.DisplayNewline()
}

func (cmd HelpCommand) displayCommonCommands() {
	cmdInfo := cmd.Actor.CommandInfos(Commands)

	for _, category := range internal.CommonHelpCategoryList {
		cmd.UI.DisplayHelpHeader(category.CategoryName)
		table := [][]string{}

		for _, row := range category.CommandList {
			finalRow := []string{}

			for _, command := range row {
				separator := ""
				if info, ok := cmdInfo[command]; ok {
					if len(info.Alias) > 0 {
						separator = ","
					}
					finalRow = append(finalRow, fmt.Sprintf("%s%s%s\t", info.Name, separator, info.Alias))
				}
			}

			table = append(table, finalRow)
		}

		cmd.UI.DisplayTable("   ", table)
		cmd.UI.DisplayNewline()
	}

	pluginCommands := cmd.getSortedPluginCommands()
	cmd.UI.DisplayHelpHeader("Commands offered by installed plugins:")

	size := int(math.Ceil(float64(len(pluginCommands)) / 3))
	table := make([][]string, size)
	for i, pluginCommand := range pluginCommands {
		table[i/3] = append(table[i/3], pluginCommand.Name)
	}

	cmd.UI.DisplayTable("   ", table)
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("Global options:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                         {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "--help, -h",
			"Description": "Show help",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                                 {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "-v",
			"Description": "Print API request diagnostics to stdout",
		})
	cmd.UI.DisplayNewline()
	cmd.UI.DisplayText("'cf help -a' lists all commands with short descriptions. See 'cf help <command>' to read about a specific command.")
}

func (cmd HelpCommand) displayAllCommands() {
	pluginCommands := cmd.getSortedPluginCommands()
	cmdInfo := cmd.Actor.CommandInfos(Commands)
	longestCmd := internal.LongestCommandName(cmdInfo, pluginCommands)

	for _, category := range internal.HelpCategoryList {
		cmd.UI.DisplayHelpHeader(category.CategoryName)

		for _, row := range category.CommandList {
			for _, command := range row {
				cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}}{{.Gap}}{{.CommandDescription}}",
					[]string{"CommandDescription"},
					map[string]interface{}{
						"CommandName":        cmdInfo[command].Name,
						"CommandDescription": cmdInfo[command].Description,
						"Gap":                strings.Repeat(" ", longestCmd+1-len(command)),
					})
			}

			cmd.UI.DisplayNewline()
		}

		cmd.UI.DisplayNewline()
	}

	cmd.UI.DisplayHelpHeader("INSTALLED PLUGIN COMMANDS:")
	for _, pluginCommand := range pluginCommands {
		cmd.UI.DisplayText("   {{.CommandName}}{{.Gap}}{{.CommandDescription}}", map[string]interface{}{
			"CommandName":        pluginCommand.Name,
			"CommandDescription": pluginCommand.HelpText,
			"Gap":                strings.Repeat(" ", longestCmd+1-len(pluginCommand.Name)),
		})
	}
	cmd.UI.DisplayNewline()
}

func (cmd HelpCommand) displayHelpFooter() {
	cmd.UI.DisplayHelpHeader("ENVIRONMENT VARIABLES:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                     {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_COLOR=false",
			"Description": "Do not colorize output",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}               {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_HOME=path/to/dir/",
			"Description": "Override path to default config directory",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}        {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_PLUGIN_HOME=path/to/dir/",
			"Description": "Override path to default plugin config directory",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}              {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_STAGING_TIMEOUT=15",
			"Description": "Max wait time for buildpack staging, in minutes",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}               {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_STARTUP_TIMEOUT=5",
			"Description": "Max wait time for app instance startup, in minutes",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                      {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_TRACE=true",
			"Description": "Print API request diagnostics to stdout",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}         {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_TRACE=path/to/trace.log",
			"Description": "Append API request diagnostics to a log file",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}} {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "https_proxy=proxy.example.com:8080",
			"Description": "Enable HTTP proxying for API requests",
		})
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("GLOBAL OPTIONS:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                         {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "--help, -h",
			"Description": "Show help",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                                 {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "-v",
			"Description": "Print API request diagnostics to stdout",
		})
}

func (cmd HelpCommand) displayCommand() error {
	cmdInfo, err := cmd.Actor.CommandInfoByName(Commands, cmd.OptionalArgs.CommandName)
	if err != nil {
		if err, ok := err.(v2actions.ErrorInvalidCommand); ok {
			var found bool
			if cmdInfo, found = cmd.findPlugin(); !found {
				return err
			}
		} else {
			return err
		}
	}

	cmd.UI.DisplayText("NAME:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} - {{.CommandDescription}}",
		[]string{"CommandDescription"},
		map[string]interface{}{
			"CommandName":        cmdInfo.Name,
			"CommandDescription": cmdInfo.Description,
		})

	cmd.UI.DisplayNewline()
	usageString := strings.Replace(cmdInfo.Usage, "CF_NAME", cmd.Config.BinaryName(), -1)
	cmd.UI.DisplayText("USAGE:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandUsage}}",
		[]string{"CommandUsage"},
		map[string]interface{}{
			"CommandUsage": usageString,
		})

	if cmdInfo.Alias != "" {
		cmd.UI.DisplayNewline()
		cmd.UI.DisplayText("ALIAS:")
		cmd.UI.DisplayText("   {{.Alias}}",
			map[string]interface{}{
				"Alias": cmdInfo.Alias,
			})
	}

	if len(cmdInfo.Flags) != 0 {
		cmd.UI.DisplayNewline()
		cmd.UI.DisplayText("OPTIONS:")
		nameWidth := internal.LongestFlagWidth(cmdInfo.Flags) + 6
		for _, flag := range cmdInfo.Flags {
			var name string
			if flag.Short != "" && flag.Long != "" {
				name = fmt.Sprintf("--%s, -%s", flag.Long, flag.Short)
			} else if flag.Short != "" {
				name = "-" + flag.Short
			} else {
				name = "--" + flag.Long
			}

			cmd.UI.DisplayTextWithKeyTranslations("   {{.Flags}}{{.Spaces}}{{.Description}}",
				[]string{"Description"},
				map[string]interface{}{
					"Flags":       name,
					"Spaces":      strings.Repeat(" ", nameWidth-len(name)),
					"Description": flag.Description,
				})
		}
	}

	if len(cmdInfo.RelatedCommands) > 0 {
		cmd.UI.DisplayNewline()
		cmd.UI.DisplayText("SEE ALSO:")
		cmd.UI.DisplayText("   {{.RelatedCommands}}",
			map[string]interface{}{
				"RelatedCommands": strings.Join(cmdInfo.RelatedCommands, ", "),
			})
	}

	return nil
}

func (cmd HelpCommand) findPlugin() (v2actions.CommandInfo, bool) {
	for _, pluginConfig := range cmd.Config.PluginConfig() {
		for _, command := range pluginConfig.Commands {
			if command.Name == cmd.OptionalArgs.CommandName {
				return internal.ConvertPluginToCommandInfo(command), true
			}
		}
	}

	return v2actions.CommandInfo{}, false
}

func (cmd HelpCommand) getSortedPluginCommands() config.PluginCommands {
	plugins := cmd.Config.PluginConfig()

	sortedPluginNames := sortutils.Alphabetic{}
	for plugin, _ := range plugins {
		sortedPluginNames = append(sortedPluginNames, plugin)
	}
	sort.Sort(sortedPluginNames)

	pluginCommands := config.PluginCommands{}
	for _, pluginName := range sortedPluginNames {
		sortedCommands := plugins[pluginName].Commands
		sort.Sort(sortedCommands)
		pluginCommands = append(pluginCommands, sortedCommands...)
	}

	return pluginCommands
}
<commit_msg>adjust order of common plugin commands<commit_after>package v2

import (
	"fmt"
	"math"
	"sort"
	"strings"

	"code.cloudfoundry.org/cli/actors/v2actions"
	"code.cloudfoundry.org/cli/cf"
	"code.cloudfoundry.org/cli/commands"
	"code.cloudfoundry.org/cli/commands/flags"
	"code.cloudfoundry.org/cli/commands/v2/internal"
	"code.cloudfoundry.org/cli/utils/config"
	"code.cloudfoundry.org/cli/utils/sortutils"
)

//go:generate counterfeiter . HelpActor

// HelpActor handles the business logic of the help command
type HelpActor interface {
	// CommandInfoByName returns back a help command information for the given
	// command
	CommandInfoByName(interface{}, string) (v2actions.CommandInfo, error)

	// CommandInfos returns a list of all commands
	CommandInfos(interface{}) map[string]v2actions.CommandInfo
}

type HelpCommand struct {
	UI     commands.UI
	Actor  HelpActor
	Config commands.Config

	OptionalArgs flags.CommandName `positional-args:"yes"`
	AllCommands  bool              `short:"a" description:"All available CLI commands"`
	usage        interface{}       `usage:"CF_NAME help [COMMAND]"`
}

func (cmd *HelpCommand) Setup(config commands.Config, ui commands.UI) error {
	cmd.Actor = v2actions.NewActor()
	cmd.Config = config
	cmd.UI = ui

	return nil
}

func (cmd HelpCommand) Execute(args []string) error {
	var err error
	if cmd.OptionalArgs.CommandName == "" {
		cmd.displayFullHelp()
	} else {
		err = cmd.displayCommand()
	}

	return err
}

func (cmd HelpCommand) displayFullHelp() {
	cmd.displayHelpPreamble()
	if cmd.AllCommands {
		cmd.displayAllCommands()
		cmd.displayHelpFooter()
	} else {
		cmd.displayCommonCommands()
	}
}

func (cmd HelpCommand) displayHelpPreamble() {
	cmd.UI.DisplayHelpHeader("NAME:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} - {{.CommandDescription}}",
		[]string{"CommandDescription"},
		map[string]interface{}{
			"CommandName":        cmd.Config.BinaryName(),
			"CommandDescription": "A command line tool to interact with Cloud Foundry",
		})
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("USAGE:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} {{.CommandUsage}}",
		[]string{"CommandUsage"},
		map[string]interface{}{
			"CommandName":  cmd.Config.BinaryName(),
			"CommandUsage": "[global options] command [arguments...] [command options]",
		})
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("VERSION:")
	cmd.UI.DisplayText("   {{.Version}}-{{.Time}}", map[string]interface{}{
		"Version": cf.Version,
		"Time":    cf.BuiltOnDate,
	})
	cmd.UI.DisplayNewline()
}

func (cmd HelpCommand) displayCommonCommands() {
	cmdInfo := cmd.Actor.CommandInfos(Commands)

	for _, category := range internal.CommonHelpCategoryList {
		cmd.UI.DisplayHelpHeader(category.CategoryName)
		table := [][]string{}

		for _, row := range category.CommandList {
			finalRow := []string{}

			for _, command := range row {
				separator := ""
				if info, ok := cmdInfo[command]; ok {
					if len(info.Alias) > 0 {
						separator = ","
					}
					finalRow = append(finalRow, fmt.Sprintf("%s%s%s\t", info.Name, separator, info.Alias))
				}
			}

			table = append(table, finalRow)
		}

		cmd.UI.DisplayTable("   ", table)
		cmd.UI.DisplayNewline()
	}

	pluginCommands := cmd.getSortedPluginCommands()
	cmd.UI.DisplayHelpHeader("Commands offered by installed plugins:")

	size := int(math.Ceil(float64(len(pluginCommands)) / 3))
	table := make([][]string, size)
	for i := 0; i < size; i++ {
		table[i] = make([]string, 3)
		for j := 0; j < 3; j++ {
			index := i + j*3
			if index < len(pluginCommands) {
				table[i][j] = pluginCommands[index].Name
			}
		}
	}

	cmd.UI.DisplayTable("   ", table)
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("Global options:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                         {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "--help, -h",
			"Description": "Show help",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                                 {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "-v",
			"Description": "Print API request diagnostics to stdout",
		})
	cmd.UI.DisplayNewline()
	cmd.UI.DisplayText("'cf help -a' lists all commands with short descriptions. See 'cf help <command>' to read about a specific command.")
}

func (cmd HelpCommand) displayAllCommands() {
	pluginCommands := cmd.getSortedPluginCommands()
	cmdInfo := cmd.Actor.CommandInfos(Commands)
	longestCmd := internal.LongestCommandName(cmdInfo, pluginCommands)

	for _, category := range internal.HelpCategoryList {
		cmd.UI.DisplayHelpHeader(category.CategoryName)

		for _, row := range category.CommandList {
			for _, command := range row {
				cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}}{{.Gap}}{{.CommandDescription}}",
					[]string{"CommandDescription"},
					map[string]interface{}{
						"CommandName":        cmdInfo[command].Name,
						"CommandDescription": cmdInfo[command].Description,
						"Gap":                strings.Repeat(" ", longestCmd+1-len(command)),
					})
			}
		}

		cmd.UI.DisplayNewline()
	}

	cmd.UI.DisplayHelpHeader("INSTALLED PLUGIN COMMANDS:")
	for _, pluginCommand := range pluginCommands {
		cmd.UI.DisplayText("   {{.CommandName}}{{.Gap}}{{.CommandDescription}}", map[string]interface{}{
			"CommandName":        pluginCommand.Name,
			"CommandDescription": pluginCommand.HelpText,
			"Gap":                strings.Repeat(" ", longestCmd+1-len(pluginCommand.Name)),
		})
	}
	cmd.UI.DisplayNewline()
}

func (cmd HelpCommand) displayHelpFooter() {
	cmd.UI.DisplayHelpHeader("ENVIRONMENT VARIABLES:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                     {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_COLOR=false",
			"Description": "Do not colorize output",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}               {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_HOME=path/to/dir/",
			"Description": "Override path to default config directory",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}        {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_PLUGIN_HOME=path/to/dir/",
			"Description": "Override path to default plugin config directory",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}              {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_STAGING_TIMEOUT=15",
			"Description": "Max wait time for buildpack staging, in minutes",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}               {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_STARTUP_TIMEOUT=5",
			"Description": "Max wait time for app instance startup, in minutes",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                      {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_TRACE=true",
			"Description": "Print API request diagnostics to stdout",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}         {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_TRACE=path/to/trace.log",
			"Description": "Append API request diagnostics to a log file",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}} {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "https_proxy=proxy.example.com:8080",
			"Description": "Enable HTTP proxying for API requests",
		})
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("GLOBAL OPTIONS:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                         {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "--help, -h",
			"Description": "Show help",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                                 {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "-v",
			"Description": "Print API request diagnostics to stdout",
		})
}

func (cmd HelpCommand) displayCommand() error {
	cmdInfo, err := cmd.Actor.CommandInfoByName(Commands, cmd.OptionalArgs.CommandName)
	if err != nil {
		if err, ok := err.(v2actions.ErrorInvalidCommand); ok {
			var found bool
			if cmdInfo, found = cmd.findPlugin(); !found {
				return err
			}
		} else {
			return err
		}
	}

	cmd.UI.DisplayText("NAME:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} - {{.CommandDescription}}",
		[]string{"CommandDescription"},
		map[string]interface{}{
			"CommandName":        cmdInfo.Name,
			"CommandDescription": cmdInfo.Description,
		})

	cmd.UI.DisplayNewline()
	usageString := strings.Replace(cmdInfo.Usage, "CF_NAME", cmd.Config.BinaryName(), -1)
	cmd.UI.DisplayText("USAGE:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandUsage}}",
		[]string{"CommandUsage"},
		map[string]interface{}{
			"CommandUsage": usageString,
		})

	if cmdInfo.Alias != "" {
		cmd.UI.DisplayNewline()
		cmd.UI.DisplayText("ALIAS:")
		cmd.UI.DisplayText("   {{.Alias}}",
			map[string]interface{}{
				"Alias": cmdInfo.Alias,
			})
	}

	if len(cmdInfo.Flags) != 0 {
		cmd.UI.DisplayNewline()
		cmd.UI.DisplayText("OPTIONS:")
		nameWidth := internal.LongestFlagWidth(cmdInfo.Flags) + 6
		for _, flag := range cmdInfo.Flags {
			var name string
			if flag.Short != "" && flag.Long != "" {
				name = fmt.Sprintf("--%s, -%s", flag.Long, flag.Short)
			} else if flag.Short != "" {
				name = "-" + flag.Short
			} else {
				name = "--" + flag.Long
			}

			cmd.UI.DisplayTextWithKeyTranslations("   {{.Flags}}{{.Spaces}}{{.Description}}",
				[]string{"Description"},
				map[string]interface{}{
					"Flags":       name,
					"Spaces":      strings.Repeat(" ", nameWidth-len(name)),
					"Description": flag.Description,
				})
		}
	}

	if len(cmdInfo.RelatedCommands) > 0 {
		cmd.UI.DisplayNewline()
		cmd.UI.DisplayText("SEE ALSO:")
		cmd.UI.DisplayText("   {{.RelatedCommands}}",
			map[string]interface{}{
				"RelatedCommands": strings.Join(cmdInfo.RelatedCommands, ", "),
			})
	}

	return nil
}

func (cmd HelpCommand) findPlugin() (v2actions.CommandInfo, bool) {
	for _, pluginConfig := range cmd.Config.PluginConfig() {
		for _, command := range pluginConfig.Commands {
			if command.Name == cmd.OptionalArgs.CommandName {
				return internal.ConvertPluginToCommandInfo(command), true
			}
		}
	}

	return v2actions.CommandInfo{}, false
}

func (cmd HelpCommand) getSortedPluginCommands() config.PluginCommands {
	plugins := cmd.Config.PluginConfig()

	sortedPluginNames := sortutils.Alphabetic{}
	for plugin, _ := range plugins {
		sortedPluginNames = append(sortedPluginNames, plugin)
	}
	sort.Sort(sortedPluginNames)

	pluginCommands := config.PluginCommands{}
	for _, pluginName := range sortedPluginNames {
		sortedCommands := plugins[pluginName].Commands
		sort.Sort(sortedCommands)
		pluginCommands = append(pluginCommands, sortedCommands...)
	}

	return pluginCommands
}
<|endoftext|>


<commit_before>package tests_test

import (
	"strings"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	tests "kubevirt.io/kubevirt-ansible/tests/framework"
	ktests "kubevirt.io/kubevirt/tests"
)

var _ = Describe("Node Eviction", func() {
	ns := ktests.NamespaceTestDefault

	var vmi tests.VirtualMachine

	BeforeEach(func() {
		ktests.BeforeTestCleanup()

		args := []string{"get", "node", "--selector=node-role.kubernetes.io/compute=true", "--output=jsonpath={.items..metadata.name}"}
		output, _, err := ktests.RunCommand("oc", args...)
		Expect(err).ToNot(HaveOccurred())

		var count int
		nodes := strings.Fields(output)
		for _, node := range nodes {
			args := []string{"get", "node", node}
			output, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			if !strings.Contains(output, "SchedulingDisabled") && !strings.Contains(output, "NotReady") {
				count++
			}
		}
		if count < 2 {
			Skip("not enough schedulable nodes for this testing")
		}

	})

	AfterEach(func() {
		// nodes can be in drained status after some tests is failed, ensure uncordoning them after each tests.
		uncordonNodes()
	})

	Describe("VirtualMachineInstance Evictions", func() {
		BeforeEach(func() {
			vmi.Manifest = "tests/manifests/vmi-ephemeral.yaml"
			vmi.Name = "vmi-ephemeral"
		})
		It("virtualmachine instance will not be re-scheduled", func() {
			By("Create the virtualmachine instance")
			args := []string{"create", "-n", ns, "-f", vmi.Manifest}
			_, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Check that the virtualmachine instance is running")
			args = []string{"get", "-n", ns, "vmi", vmi.Name, "--template", "{{.status.phase}}"}
			Eventually(func() string {
				output, _, err := ktests.RunCommand("oc", args...)
				Expect(err).ToNot(HaveOccurred())
				return output
			}, time.Minute*10).Should(Equal("Running"))

			By("Retrieve the vmi's node")
			args = []string{"get", "-n", ns, "vmi", vmi.Name, "--template", "{{.status.nodeName}}"}
			nodeName, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Drain the vmi's node")
			args = []string{"adm", "drain", nodeName, "--delete-local-data", "--ignore-daemonsets", "--force", "--pod-selector=kubevirt.io=virt-launcher"}
			_, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Verify that the virtual machine instance is Failed")
			args = []string{"get", "-n", ns, "vmi", vmi.Name, "--template", "{{.status.phase}}"}
			output, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())
			Expect(output).To(Equal("Failed"))

			By("Verify that the vmi's node is unschedulable")
			args = []string{"get", "node", nodeName, "--template", "{{.spec.unschedulable}}"}
			output, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())
			Expect(output).To(Equal("true"))

			By("Uncordon the vmi's node")
			args = []string{"adm", "uncordon", nodeName}
			_, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Verify that the node is schedulable again")
			args = []string{"get", "node", nodeName}
			output, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())
			Expect(output).ToNot(ContainSubstring("SchedulingDisabled"))
		})
	})

	PDescribe("VirtualMachineInstanceReplicaSet Eviction", func() {
		BeforeEach(func() {
			vmi.Name = "vmi-replicaset-cirros"
			vmi.Manifest = "tests/manifests/vmi-replicaset-cirros.yaml"
		})
		It("virtualmachineinstance owned by a replicaset will be re-scheduled", func() {
			By("Create the virtualmachine instance replicaset")
			args := []string{"create", "-n", ns, "-f", vmi.Manifest}
			_, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Check that the virtualmachine instance is running")
			args = []string{"get", "-n", ns, "vmi", "--selector=kubevirt.io/vmReplicaSet=vmi-replicaset-cirros", "--output=jsonpath={.items..metadata.name}"}
			vmis, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			vmiList := strings.Fields(vmis)
			for _, vmi := range vmiList {
				args = []string{"get", "-n", ns, "vmi", vmi, "--template", "{{.status.phase}}"}
				Eventually(func() string {
					output, _, err := ktests.RunCommand("oc", args...)
					Expect(err).ToNot(HaveOccurred())
					return output
				}, time.Minute*10).Should(Equal("Running"))
			}

			By("Retrieve the vmi's node")
			args = []string{"get", "-n", ns, "vmi", vmiList[0], "--template", "{{.status.nodeName}}"}
			nodeName, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Drain the vmi's node")
			args = []string{"adm", "drain", nodeName, "--delete-local-data", "--ignore-daemonsets=true", "--force", "--pod-selector=kubevirt.io=virt-launcher"}
			_, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Verify that the vmi's node is unschedulable")
			args = []string{"get", "node", nodeName, "--template", "{{.spec.unschedulable}}"}
			Eventually(func() string {
				output, _, err := ktests.RunCommand("oc", args...)
				Expect(err).ToNot(HaveOccurred())
				return output
			}, time.Minute*2).Should(Equal("true"))

			By("Verify that the vmi is running on other node")
			args = []string{"get", "-n", ns, "vmi", "--selector=kubevirt.io/vmReplicaSet=vmi-replicaset-cirros", "--output=jsonpath={.items..metadata.name}"}
			vmis, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			// It's verify all VM instance are running, not just verify new VM instance status.
			vmiList = strings.Fields(vmis)
			for _, vmi := range vmiList {
				args = []string{"get", "-n", ns, "vmi", vmi, "--template", "{{.status.phase}}"}
				Eventually(func() string {
					output, _, err := ktests.RunCommand("oc", args...)
					Expect(err).ToNot(HaveOccurred())
					return output
				}, time.Minute*2).Should(Equal("Running"))
			}

			By("Uncordon the vmi's node")
			args = []string{"adm", "uncordon", nodeName}
			_, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Verify that the node is schedulable again")
			args = []string{"get", "node", nodeName}
			output, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())
			Expect(output).ToNot(ContainSubstring("SchedulingDisabled"))
		})
	})

	Describe("VirtualMachine Eviction", func() {
		BeforeEach(func() {
			vmi.Manifest = "tests/manifests/vm-cirros.yaml"
			vmi.Name = "vm-cirros"
		})
		It("virtualmachineinstance owned by a virtual machine will be re-scheduled", func() {
			By("Create the virtualmachine instance's vm")
			args := []string{"create", "-n", ns, "-f", vmi.Manifest}
			_, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Start the virtual machine via virtctl")
			args = []string{"-n", ns, "start", vmi.Name}
			_, _, err = ktests.RunCommand("virtctl", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Check that the virtualmachine instance is running")
			args = []string{"get", "-n", ns, "vmi", vmi.Name, "--template", "{{.status.phase}}"}
			Eventually(func() string {
				output, _, err := ktests.RunCommand("oc", args...)
				Expect(err).ToNot(HaveOccurred())
				return output
			}, time.Minute*10).Should(Equal("Running"))

			By("Retrieve the vmi's node")
			args = []string{"get", "-n", ns, "vmi", vmi.Name, "--template", "{{.status.nodeName}}"}
			nodeName, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Drain the vmi's node")
			args = []string{"adm", "drain", nodeName, "--delete-local-data", "--ignore-daemonsets=true", "--force", "--pod-selector=kubevirt.io=virt-launcher"}
			_, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Verify that the vmi's node is unschedulable")
			args = []string{"get", "node", nodeName, "--template", "{{.spec.unschedulable}}"}
			Eventually(func() (string, string, error) {
				return ktests.RunCommand("oc", args...)
			}, time.Minute*2).Should(Equal("true"))

			By("Verify that the vmi is running on other node")
			args = []string{"get", "-n", ns, "vmi", vmi.Name, "--template", "{{.status.phase}}"}
			Eventually(func() string {
				output, _, err := ktests.RunCommand("oc", args...)
				Expect(err).ToNot(HaveOccurred())
				return output
			}, time.Minute*2).Should(Equal("Running"))

			By("Uncordon the vmi's node")
			args = []string{"adm", "uncordon", nodeName}
			_, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Verify that the node is schedulable again")
			args = []string{"get", "node", nodeName}
			output, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())
			Expect(output).ToNot(ContainSubstring("SchedulingDisabled"))
		})
	})
})

func uncordonNodes() {
	args := []string{"get", "node", "--selector=node-role.kubernetes.io/compute=true", "--output=jsonpath={.items..metadata.name}"}
	output, _, err := ktests.RunCommand("oc", args...)
	Expect(err).ToNot(HaveOccurred())

	nodes := strings.Fields(output)
	for _, node := range nodes {
		args := []string{"get", "node", node}
		output, _, err := ktests.RunCommand("oc", args...)
		Expect(err).ToNot(HaveOccurred())

		if strings.Contains(output, "SchedulingDisabled") {
			args = []string{"adm", "uncordon", node}
			_, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())
		}
	}
}
<commit_msg>Revert pending test in node eviction (#608)<commit_after>package tests_test

import (
	"strings"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	tests "kubevirt.io/kubevirt-ansible/tests/framework"
	ktests "kubevirt.io/kubevirt/tests"
)

var _ = Describe("Node Eviction", func() {
	ns := ktests.NamespaceTestDefault

	var vmi tests.VirtualMachine

	BeforeEach(func() {
		ktests.BeforeTestCleanup()

		args := []string{"get", "node", "--selector=node-role.kubernetes.io/compute=true", "--output=jsonpath={.items..metadata.name}"}
		output, _, err := ktests.RunCommand("oc", args...)
		Expect(err).ToNot(HaveOccurred())

		var count int
		nodes := strings.Fields(output)
		for _, node := range nodes {
			args := []string{"get", "node", node}
			output, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			if !strings.Contains(output, "SchedulingDisabled") && !strings.Contains(output, "NotReady") {
				count++
			}
		}
		if count < 2 {
			Skip("not enough schedulable nodes for this testing")
		}

	})

	AfterEach(func() {
		// nodes can be in drained status after some tests is failed, ensure uncordoning them after each tests.
		uncordonNodes()
	})

	Describe("VirtualMachineInstance Evictions", func() {
		BeforeEach(func() {
			vmi.Manifest = "tests/manifests/vmi-ephemeral.yaml"
			vmi.Name = "vmi-ephemeral"
		})
		It("virtualmachine instance will not be re-scheduled", func() {
			By("Create the virtualmachine instance")
			args := []string{"create", "-n", ns, "-f", vmi.Manifest}
			_, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Check that the virtualmachine instance is running")
			args = []string{"get", "-n", ns, "vmi", vmi.Name, "--template", "{{.status.phase}}"}
			Eventually(func() string {
				output, _, err := ktests.RunCommand("oc", args...)
				Expect(err).ToNot(HaveOccurred())
				return output
			}, time.Minute*10).Should(Equal("Running"))

			By("Retrieve the vmi's node")
			args = []string{"get", "-n", ns, "vmi", vmi.Name, "--template", "{{.status.nodeName}}"}
			nodeName, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Drain the vmi's node")
			args = []string{"adm", "drain", nodeName, "--delete-local-data", "--ignore-daemonsets", "--force", "--pod-selector=kubevirt.io=virt-launcher"}
			_, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Verify that the virtual machine instance is Failed")
			args = []string{"get", "-n", ns, "vmi", vmi.Name, "--template", "{{.status.phase}}"}
			output, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())
			Expect(output).To(Equal("Failed"))

			By("Verify that the vmi's node is unschedulable")
			args = []string{"get", "node", nodeName, "--template", "{{.spec.unschedulable}}"}
			output, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())
			Expect(output).To(Equal("true"))

			By("Uncordon the vmi's node")
			args = []string{"adm", "uncordon", nodeName}
			_, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Verify that the node is schedulable again")
			args = []string{"get", "node", nodeName}
			output, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())
			Expect(output).ToNot(ContainSubstring("SchedulingDisabled"))
		})
	})

	Describe("VirtualMachineInstanceReplicaSet Eviction", func() {
		BeforeEach(func() {
			vmi.Name = "vmi-replicaset-cirros"
			vmi.Manifest = "tests/manifests/vmi-replicaset-cirros.yaml"
		})
		It("virtualmachineinstance owned by a replicaset will be re-scheduled", func() {
			By("Create the virtualmachine instance replicaset")
			args := []string{"create", "-n", ns, "-f", vmi.Manifest}
			_, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Check that the virtualmachine instance is running")
			args = []string{"get", "-n", ns, "vmi", "--selector=kubevirt.io/vmReplicaSet=vmi-replicaset-cirros", "--output=jsonpath={.items..metadata.name}"}
			vmis, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			vmiList := strings.Fields(vmis)
			for _, vmi := range vmiList {
				args = []string{"get", "-n", ns, "vmi", vmi, "--template", "{{.status.phase}}"}
				Eventually(func() string {
					output, _, err := ktests.RunCommand("oc", args...)
					Expect(err).ToNot(HaveOccurred())
					return output
				}, time.Minute*10).Should(Equal("Running"))
			}

			By("Retrieve the vmi's node")
			args = []string{"get", "-n", ns, "vmi", vmiList[0], "--template", "{{.status.nodeName}}"}
			nodeName, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Drain the vmi's node")
			args = []string{"adm", "drain", nodeName, "--delete-local-data", "--ignore-daemonsets=true", "--force", "--pod-selector=kubevirt.io=virt-launcher"}
			_, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Verify that the vmi's node is unschedulable")
			args = []string{"get", "node", nodeName, "--template", "{{.spec.unschedulable}}"}
			Eventually(func() string {
				output, _, err := ktests.RunCommand("oc", args...)
				Expect(err).ToNot(HaveOccurred())
				return output
			}, time.Minute*2).Should(Equal("true"))

			By("Verify that the vmi is running on other node")
			args = []string{"get", "-n", ns, "vmi", "--selector=kubevirt.io/vmReplicaSet=vmi-replicaset-cirros", "--output=jsonpath={.items..metadata.name}"}
			vmis, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			// It's verify all VM instance are running, not just verify new VM instance status.
			vmiList = strings.Fields(vmis)
			for _, vmi := range vmiList {
				args = []string{"get", "-n", ns, "vmi", vmi, "--template", "{{.status.phase}}"}
				Eventually(func() string {
					output, _, err := ktests.RunCommand("oc", args...)
					Expect(err).ToNot(HaveOccurred())
					return output
				}, time.Minute*2).Should(Equal("Running"))
			}

			By("Uncordon the vmi's node")
			args = []string{"adm", "uncordon", nodeName}
			_, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Verify that the node is schedulable again")
			args = []string{"get", "node", nodeName}
			output, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())
			Expect(output).ToNot(ContainSubstring("SchedulingDisabled"))
		})
	})

	Describe("VirtualMachine Eviction", func() {
		BeforeEach(func() {
			vmi.Manifest = "tests/manifests/vm-cirros.yaml"
			vmi.Name = "vm-cirros"
		})
		It("virtualmachineinstance owned by a virtual machine will be re-scheduled", func() {
			By("Create the virtualmachine instance's vm")
			args := []string{"create", "-n", ns, "-f", vmi.Manifest}
			_, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Start the virtual machine via virtctl")
			args = []string{"-n", ns, "start", vmi.Name}
			_, _, err = ktests.RunCommand("virtctl", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Check that the virtualmachine instance is running")
			args = []string{"get", "-n", ns, "vmi", vmi.Name, "--template", "{{.status.phase}}"}
			Eventually(func() string {
				output, _, err := ktests.RunCommand("oc", args...)
				Expect(err).ToNot(HaveOccurred())
				return output
			}, time.Minute*10).Should(Equal("Running"))

			By("Retrieve the vmi's node")
			args = []string{"get", "-n", ns, "vmi", vmi.Name, "--template", "{{.status.nodeName}}"}
			nodeName, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Drain the vmi's node")
			args = []string{"adm", "drain", nodeName, "--delete-local-data", "--ignore-daemonsets=true", "--force", "--pod-selector=kubevirt.io=virt-launcher"}
			_, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Verify that the vmi's node is unschedulable")
			args = []string{"get", "node", nodeName, "--template", "{{.spec.unschedulable}}"}
			Eventually(func() (string, string, error) {
				return ktests.RunCommand("oc", args...)
			}, time.Minute*2).Should(Equal("true"))

			By("Verify that the vmi is running on other node")
			args = []string{"get", "-n", ns, "vmi", vmi.Name, "--template", "{{.status.phase}}"}
			Eventually(func() string {
				output, _, err := ktests.RunCommand("oc", args...)
				Expect(err).ToNot(HaveOccurred())
				return output
			}, time.Minute*2).Should(Equal("Running"))

			By("Uncordon the vmi's node")
			args = []string{"adm", "uncordon", nodeName}
			_, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())

			By("Verify that the node is schedulable again")
			args = []string{"get", "node", nodeName}
			output, _, err := ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())
			Expect(output).ToNot(ContainSubstring("SchedulingDisabled"))
		})
	})
})

func uncordonNodes() {
	args := []string{"get", "node", "--selector=node-role.kubernetes.io/compute=true", "--output=jsonpath={.items..metadata.name}"}
	output, _, err := ktests.RunCommand("oc", args...)
	Expect(err).ToNot(HaveOccurred())

	nodes := strings.Fields(output)
	for _, node := range nodes {
		args := []string{"get", "node", node}
		output, _, err := ktests.RunCommand("oc", args...)
		Expect(err).ToNot(HaveOccurred())

		if strings.Contains(output, "SchedulingDisabled") {
			args = []string{"adm", "uncordon", node}
			_, _, err = ktests.RunCommand("oc", args...)
			Expect(err).ToNot(HaveOccurred())
		}
	}
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2017 Red Hat, Inc.
 *
 */

package tests_test

import (
	"flag"
	"strings"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"k8s.io/apimachinery/pkg/runtime"

	"kubevirt.io/kubevirt/pkg/api/v1"
	"kubevirt.io/kubevirt/pkg/kubecli"
	"kubevirt.io/kubevirt/pkg/virt-controller/services"
	"kubevirt.io/kubevirt/tests"
)

var _ = Describe("RegistryDisk", func() {

	flag.Parse()

	virtClient, err := kubecli.GetKubevirtClient()
	tests.PanicOnError(err)

	BeforeEach(func() {
		tests.BeforeTestCleanup()
	})

	LaunchVM := func(vm *v1.VirtualMachine) runtime.Object {
		obj, err := virtClient.RestClient().Post().Resource("virtualmachines").Namespace(tests.NamespaceTestDefault).Body(vm).Do().Get()
		Expect(err).To(BeNil())
		return obj
	}

	VerifyRegistryDiskVM := func(vm *v1.VirtualMachine, obj runtime.Object) {
		_, ok := obj.(*v1.VirtualMachine)
		Expect(ok).To(BeTrue(), "Object is not of type *v1.VM")
		tests.WaitForSuccessfulVMStart(obj)

		// Verify Registry Disks are Online
		pods, err := virtClient.CoreV1().Pods(tests.NamespaceTestDefault).List(services.UnfinishedVMPodSelector(vm))
		Expect(err).To(BeNil())
		disksFound := 0
		for _, pod := range pods.Items {
			if pod.ObjectMeta.DeletionTimestamp != nil {
				continue
			}
			for _, containerStatus := range pod.Status.ContainerStatuses {
				if strings.Contains(containerStatus.Name, "disk") == false {
					// only check readiness of disk containers
					continue
				}
				disksFound++
			}
			break
		}
		Expect(disksFound).To(Equal(1))
	}

	Context("Ephemeral RegistryDisk", func() {
		It("should be able to start and stop the same VM multiple times.", func(done Done) {
			vm := tests.NewRandomVMWithEphemeralDisk("kubevirt/cirros-registry-disk-demo:devel")
			num := 3
			for i := 0; i < num; i++ {
				obj, err := virtClient.RestClient().Post().Resource("virtualmachines").Namespace(tests.NamespaceTestDefault).Body(vm).Do().Get()
				Expect(err).To(BeNil())
				tests.WaitForSuccessfulVMStartWithTimeout(obj, 60)
				_, err = virtClient.RestClient().Delete().Resource("virtualmachines").Namespace(vm.GetObjectMeta().GetNamespace()).Name(vm.GetObjectMeta().GetName()).Do().Get()
				Expect(err).To(BeNil())
				tests.NewObjectEventWatcher(obj).SinceWatchedObjectResourceVersion().WaitFor(tests.NormalEvent, v1.Deleted)
			}
			close(done)
		}, 180)

		It("should launch multiple VMs using ephemeral registry disks", func(done Done) {
			num := 5
			vms := make([]*v1.VirtualMachine, 0, num)
			objs := make([]runtime.Object, 0, num)
			for i := 0; i < num; i++ {
				vm := tests.NewRandomVMWithEphemeralDisk("kubevirt/cirros-registry-disk-demo:devel")
				obj := LaunchVM(vm)
				vms = append(vms, vm)
				objs = append(objs, obj)
			}

			for idx, vm := range vms {
				VerifyRegistryDiskVM(vm, objs[idx])
			}

			close(done)
		}, 120) // Timeout is long because this test involves multiple parallel VM launches.
	})
})
<commit_msg>Increase registry-disk test timeout to account for k8s pod deletion<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2017 Red Hat, Inc.
 *
 */

package tests_test

import (
	"flag"
	"strings"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"k8s.io/apimachinery/pkg/runtime"

	"kubevirt.io/kubevirt/pkg/api/v1"
	"kubevirt.io/kubevirt/pkg/kubecli"
	"kubevirt.io/kubevirt/pkg/virt-controller/services"
	"kubevirt.io/kubevirt/tests"
)

var _ = Describe("RegistryDisk", func() {

	flag.Parse()

	virtClient, err := kubecli.GetKubevirtClient()
	tests.PanicOnError(err)

	BeforeEach(func() {
		tests.BeforeTestCleanup()
	})

	LaunchVM := func(vm *v1.VirtualMachine) runtime.Object {
		obj, err := virtClient.RestClient().Post().Resource("virtualmachines").Namespace(tests.NamespaceTestDefault).Body(vm).Do().Get()
		Expect(err).To(BeNil())
		return obj
	}

	VerifyRegistryDiskVM := func(vm *v1.VirtualMachine, obj runtime.Object) {
		_, ok := obj.(*v1.VirtualMachine)
		Expect(ok).To(BeTrue(), "Object is not of type *v1.VM")
		tests.WaitForSuccessfulVMStart(obj)

		// Verify Registry Disks are Online
		pods, err := virtClient.CoreV1().Pods(tests.NamespaceTestDefault).List(services.UnfinishedVMPodSelector(vm))
		Expect(err).To(BeNil())
		disksFound := 0
		for _, pod := range pods.Items {
			if pod.ObjectMeta.DeletionTimestamp != nil {
				continue
			}
			for _, containerStatus := range pod.Status.ContainerStatuses {
				if strings.Contains(containerStatus.Name, "disk") == false {
					// only check readiness of disk containers
					continue
				}
				disksFound++
			}
			break
		}
		Expect(disksFound).To(Equal(1))
	}

	Context("Ephemeral RegistryDisk", func() {
		It("should be able to start and stop the same VM multiple times.", func(done Done) {
			vm := tests.NewRandomVMWithEphemeralDisk("kubevirt/cirros-registry-disk-demo:devel")
			num := 3
			for i := 0; i < num; i++ {
				obj, err := virtClient.RestClient().Post().Resource("virtualmachines").Namespace(tests.NamespaceTestDefault).Body(vm).Do().Get()
				Expect(err).To(BeNil())
				tests.WaitForSuccessfulVMStartWithTimeout(obj, 120)
				_, err = virtClient.RestClient().Delete().Resource("virtualmachines").Namespace(vm.GetObjectMeta().GetNamespace()).Name(vm.GetObjectMeta().GetName()).Do().Get()
				Expect(err).To(BeNil())
				tests.NewObjectEventWatcher(obj).SinceWatchedObjectResourceVersion().WaitFor(tests.NormalEvent, v1.Deleted)
			}
			close(done)
		}, 200)

		It("should launch multiple VMs using ephemeral registry disks", func(done Done) {
			num := 5
			vms := make([]*v1.VirtualMachine, 0, num)
			objs := make([]runtime.Object, 0, num)
			for i := 0; i < num; i++ {
				vm := tests.NewRandomVMWithEphemeralDisk("kubevirt/cirros-registry-disk-demo:devel")
				obj := LaunchVM(vm)
				vms = append(vms, vm)
				objs = append(objs, obj)
			}

			for idx, vm := range vms {
				VerifyRegistryDiskVM(vm, objs[idx])
			}

			close(done)
		}, 120) // Timeout is long because this test involves multiple parallel VM launches.
	})
})
<|endoftext|>


<commit_before>package command

import (
  "fmt"
  "encoding/json"
  "os"
  "os/signal"
  "time"

  "github.com/jwaldrip/odin/cli"
  "github.com/hashicorp/consul/api"
)

type CascadeEvent struct {
  Source string `json:"source"`
  Msg string `json:"msg"`
  Ref string `json:"ref"`
}

var Cm = cli.NewSubCommand("cm", "Config management operations", cmRun)

func init() {
  Cm.DefineParams("action")
  Cm.DefineStringFlag("role", "", "filter by role")
  Cm.AliasFlag('r', "role")

  Cm.SetLongDescription(`
Run CM on member systems

Actions:
  roll - ordered synchronous run
  local - run CM locally only
  single <nodename> - run on single remote node
  `)
}

func cmRun(c cli.Command) {
  switch c.Param("action").String() {
  case "local": cmLocal(c)
  case "roll": cmRoll(c)
  case "single": cmSingle(c)
  default: cli.ShowUsage(c)
  }
}

func cmLocal(c cli.Command) {
  client, _ := api.NewClient(api.DefaultConfig())
  agent := client.Agent()

  self, err := agent.Self()

  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  services, err := agent.Services()
  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  if _, ok := services["cascade"]; !ok {
    fmt.Println("Node not managemed by cascade")
    os.Exit(1)
  }

  fmt.Println("Running CM...")
  _cmDispatch(self["Config"]["NodeName"].(string))
}

func cmRoll(c cli.Command) {
  client, _ := api.NewClient(api.DefaultConfig())
  catalog := client.Catalog()
  session := client.Session()
  kv := client.KV()

  nodes, _, err := catalog.Service("cascade", c.Flag("role").String(), nil)

  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  se := &api.SessionEntry{
    Name: "cascade",
    TTL: "250s",
    Behavior: api.SessionBehaviorDelete,
  }

  session_id, _, err := session.Create(se, nil)
  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }
  defer session.Destroy(session_id, nil)

  key := "cascade/roll"
  user := os.Getenv("USER")

  if user == "root" && os.Getenv("SUDO_USER") != "" {
    user = os.Getenv("SUDO_USER")
  }

  value := []byte(user)
  p := &api.KVPair{Key: key, Value: value, Session: session_id}
  if work, _, err := kv.Acquire(p, nil); err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  } else if !work {
    fmt.Println("Failed to acquire roll lock")

    pair, _, err := kv.Get(key, nil)
    if err != nil {
      fmt.Println("err: ", err)
    }

    fmt.Println("user:", string(pair.Value[:]), "has the lock")

    os.Exit(1)
  }

  ch := make(chan os.Signal, 1)
  signal.Notify(ch, os.Interrupt)
  go func(){
    for range ch {
      if work, _, err := kv.Release(p, nil); err != nil {
        fmt.Println("err: ", err)
        os.Exit(1)
      } else if !work {
        fmt.Println("failed to release lock")
        os.Exit(1)
      }
      os.Exit(0)
    }
  }()

  fmt.Printf("Rolling (%v) nodes..\n", len(nodes))
  for _,node := range nodes {
    dispatch := _cmDispatch(node.Node)
    if dispatch == "fail" {
      fmt.Println("roll stopped")
      os.Exit(1)
    }

    renew, _, err := session.Renew(session_id, nil)
    if err != nil {
      fmt.Println("err: ", err)
      os.Exit(1)
    }

    if renew == nil {
       fmt.Println("session renewal failed")
    }
  }
}

func cmSingle(c cli.Command) {
  client, _ := api.NewClient(api.DefaultConfig())
  catalog := client.Catalog()

  node, _, err := catalog.Node(c.Arg(0).String(), nil)

  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  if node == nil {
    fmt.Println("node not found")
    os.Exit(1)
  }

  if node.Services["cascade"] == nil {
    fmt.Println("node not managed by cascade")
    os.Exit(1)
  }

  fmt.Println("Running CM...")
  _cmDispatch(c.Arg(0).String())
}

func _cmDispatch(host string) string {
  client, _ := api.NewClient(api.DefaultConfig())
  event := client.Event()

  cascadeEvent := CascadeEvent{"cascade cli", "run", ""}
  payload, _ := json.Marshal(cascadeEvent)
  params := &api.UserEvent{Name: "cascade.cm", Payload: payload, NodeFilter: host}

  id, _, err := event.Fire(params, nil)

  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  seen := make(map[string]bool)

  for {
    events, _, err := event.List("cascade.cm", nil)
    if err != nil {
      fmt.Println("err: ", err)
      os.Exit(1)
    }

    for _, event := range events {
      var e CascadeEvent
      err := json.Unmarshal(event.Payload, &e)

      if err != nil {
        fmt.Println("err: ", err)
      }

      if e.Ref == id {
        if !seen[event.ID] {
          if len(seen) == 0 {
            fmt.Println(e.Source)
          }
          fmt.Println("  -", e.Msg)
          seen[event.ID] = true
        }
        if e.Msg == "success" || e.Msg == "fail" {
          return e.Msg
        }
      }
    }

    time.Sleep(1 * time.Second)
  }
}
<commit_msg>check for nil key<commit_after>package command

import (
  "fmt"
  "encoding/json"
  "os"
  "os/signal"
  "time"

  "github.com/jwaldrip/odin/cli"
  "github.com/hashicorp/consul/api"
)

type CascadeEvent struct {
  Source string `json:"source"`
  Msg string `json:"msg"`
  Ref string `json:"ref"`
}

var Cm = cli.NewSubCommand("cm", "Config management operations", cmRun)

func init() {
  Cm.DefineParams("action")
  Cm.DefineStringFlag("role", "", "filter by role")
  Cm.AliasFlag('r', "role")

  Cm.SetLongDescription(`
Run CM on member systems

Actions:
  roll - ordered synchronous run
  local - run CM locally only
  single <nodename> - run on single remote node
  `)
}

func cmRun(c cli.Command) {
  switch c.Param("action").String() {
  case "local": cmLocal(c)
  case "roll": cmRoll(c)
  case "single": cmSingle(c)
  default: cli.ShowUsage(c)
  }
}

func cmLocal(c cli.Command) {
  client, _ := api.NewClient(api.DefaultConfig())
  agent := client.Agent()

  self, err := agent.Self()

  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  services, err := agent.Services()
  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  if _, ok := services["cascade"]; !ok {
    fmt.Println("Node not managemed by cascade")
    os.Exit(1)
  }

  fmt.Println("Running CM...")
  _cmDispatch(self["Config"]["NodeName"].(string))
}

func cmRoll(c cli.Command) {
  client, _ := api.NewClient(api.DefaultConfig())
  catalog := client.Catalog()
  session := client.Session()
  kv := client.KV()

  nodes, _, err := catalog.Service("cascade", c.Flag("role").String(), nil)

  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  se := &api.SessionEntry{
    Name: "cascade",
    TTL: "250s",
    Behavior: api.SessionBehaviorDelete,
  }

  session_id, _, err := session.Create(se, nil)
  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }
  defer session.Destroy(session_id, nil)

  key := "cascade/roll"
  user := os.Getenv("USER")

  if user == "root" && os.Getenv("SUDO_USER") != "" {
    user = os.Getenv("SUDO_USER")
  }

  value := []byte(user)
  p := &api.KVPair{Key: key, Value: value, Session: session_id}
  if work, _, err := kv.Acquire(p, nil); err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  } else if !work {
    fmt.Println("Failed to acquire roll lock")

    pair, _, err := kv.Get(key, nil)
    if err != nil {
      fmt.Println("err: ", err)
      os.Exit(1)
    }

    if pair.Value != nil {
      fmt.Println("user:", string(pair.Value[:]), "has the lock")
    }
    os.Exit(1)
  }

  ch := make(chan os.Signal, 1)
  signal.Notify(ch, os.Interrupt)
  go func(){
    for range ch {
      if work, _, err := kv.Release(p, nil); err != nil {
        fmt.Println("err: ", err)
        os.Exit(1)
      } else if !work {
        fmt.Println("failed to release lock")
        os.Exit(1)
      }
      os.Exit(0)
    }
  }()

  fmt.Printf("Rolling (%v) nodes..\n", len(nodes))
  for _,node := range nodes {
    dispatch := _cmDispatch(node.Node)
    if dispatch == "fail" {
      fmt.Println("roll stopped")
      os.Exit(1)
    }

    renew, _, err := session.Renew(session_id, nil)
    if err != nil {
      fmt.Println("err: ", err)
      os.Exit(1)
    }

    if renew == nil {
       fmt.Println("session renewal failed")
    }
  }
}

func cmSingle(c cli.Command) {
  client, _ := api.NewClient(api.DefaultConfig())
  catalog := client.Catalog()

  node, _, err := catalog.Node(c.Arg(0).String(), nil)

  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  if node == nil {
    fmt.Println("node not found")
    os.Exit(1)
  }

  if node.Services["cascade"] == nil {
    fmt.Println("node not managed by cascade")
    os.Exit(1)
  }

  fmt.Println("Running CM...")
  _cmDispatch(c.Arg(0).String())
}

func _cmDispatch(host string) string {
  client, _ := api.NewClient(api.DefaultConfig())
  event := client.Event()

  cascadeEvent := CascadeEvent{"cascade cli", "run", ""}
  payload, _ := json.Marshal(cascadeEvent)
  params := &api.UserEvent{Name: "cascade.cm", Payload: payload, NodeFilter: host}

  id, _, err := event.Fire(params, nil)

  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  seen := make(map[string]bool)

  for {
    events, _, err := event.List("cascade.cm", nil)
    if err != nil {
      fmt.Println("err: ", err)
      os.Exit(1)
    }

    for _, event := range events {
      var e CascadeEvent
      err := json.Unmarshal(event.Payload, &e)

      if err != nil {
        fmt.Println("err: ", err)
      }

      if e.Ref == id {
        if !seen[event.ID] {
          if len(seen) == 0 {
            fmt.Println(e.Source)
          }
          fmt.Println("  -", e.Msg)
          seen[event.ID] = true
        }
        if e.Msg == "success" || e.Msg == "fail" {
          return e.Msg
        }
      }
    }

    time.Sleep(1 * time.Second)
  }
}
<|endoftext|>