go.ligato.io/vpp-agent/v3@v3.5.0/tests/e2e/e2etest/options.go (about)

     1  // Copyright (c) 2020 Pantheon.tech
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at:
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package e2etest
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"path/filepath"
    21  	"strings"
    22  
    23  	docker "github.com/fsouza/go-dockerclient"
    24  	"github.com/onsi/gomega"
    25  	"go.ligato.io/cn-infra/v2/logging"
    26  )
    27  
    28  // SetupOpt is options data holder for customizing setup of tests
    29  type SetupOpt struct {
    30  	AgentOptMods   []AgentOptModifier
    31  	EtcdOptMods    []EtcdOptModifier
    32  	DNSOptMods     []DNSOptModifier
    33  	SetupAgent     bool
    34  	SetupEtcd      bool
    35  	SetupDNSServer bool
    36  
    37  	ctx *TestCtx
    38  }
    39  
    40  // AgentOpt is options data holder for customizing setup of agent
    41  type AgentOpt struct {
    42  	Runtime             ComponentRuntime
    43  	RuntimeStartOptions RuntimeStartOptionsFunc
    44  	Name                string
    45  	Image               string
    46  	Env                 []string
    47  	UseEtcd             bool
    48  	InitialResync       bool
    49  	ContainerOptsHook   func(*docker.CreateContainerOptions)
    50  }
    51  
    52  // MicroserviceOpt is options data holder for customizing setup of microservice
    53  type MicroserviceOpt struct {
    54  	Runtime             ComponentRuntime
    55  	RuntimeStartOptions RuntimeStartOptionsFunc
    56  	Name                string
    57  	ContainerOptsHook   func(*docker.CreateContainerOptions)
    58  }
    59  
    60  // EtcdOpt is options data holder for customizing setup of ETCD
    61  type EtcdOpt struct {
    62  	Runtime                       ComponentRuntime
    63  	RuntimeStartOptions           RuntimeStartOptionsFunc
    64  	UseHTTPS                      bool
    65  	UseTestContainerForNetworking bool
    66  }
    67  
    68  // DNSOpt is options data holder for customizing setup of DNS server
    69  type DNSOpt struct {
    70  	Runtime             ComponentRuntime
    71  	RuntimeStartOptions RuntimeStartOptionsFunc
    72  
    73  	// DomainNameSuffix is common suffix of all static dns entries configured in hostsConfig
    74  	DomainNameSuffix string
    75  	// HostsConfig is content of configuration of static DNS entries in hosts file format
    76  	HostsConfig string
    77  }
    78  
    79  // RuntimeStartOptionsFunc is function that provides component runtime start options
    80  type RuntimeStartOptionsFunc func(ctx *TestCtx, options interface{}) (interface{}, error)
    81  
    82  // PingOpt are options for pinging command.
    83  type PingOpt struct {
    84  	AllowedLoss int    // percentage of allowed loss for success
    85  	SourceIface string // outgoing interface name
    86  	MaxTimeout  int    // timeout in seconds before ping exits
    87  	Count       int    // number of pings
    88  }
    89  
    90  // SetupOptModifier is function customizing general setup options
    91  type SetupOptModifier func(*SetupOpt)
    92  
    93  // AgentOptModifier is function customizing Agent setup options
    94  type AgentOptModifier func(*AgentOpt)
    95  
    96  // MicroserviceOptModifier is function customizing Microservice setup options
    97  type MicroserviceOptModifier func(*MicroserviceOpt)
    98  
    99  // EtcdOptModifier is function customizing ETCD setup options
   100  type EtcdOptModifier func(*EtcdOpt)
   101  
   102  // DNSOptModifier is function customizing DNS server setup options
   103  type DNSOptModifier func(*DNSOpt)
   104  
   105  // PingOptModifier is modifiers of pinging options
   106  type PingOptModifier func(*PingOpt)
   107  
   108  // DefaultSetupOpt creates default values for SetupOpt
   109  func DefaultSetupOpt(testCtx *TestCtx) *SetupOpt {
   110  	opt := &SetupOpt{
   111  		AgentOptMods:   nil,
   112  		EtcdOptMods:    nil,
   113  		DNSOptMods:     nil,
   114  		SetupAgent:     true,
   115  		SetupEtcd:      false,
   116  		SetupDNSServer: false,
   117  		ctx:            testCtx,
   118  	}
   119  	return opt
   120  }
   121  
   122  // DefaultEtcdOpt creates default values for EtcdOpt
   123  func DefaultEtcdOpt(ctx *TestCtx) *EtcdOpt {
   124  	return &EtcdOpt{
   125  		Runtime: &ContainerRuntime{
   126  			ctx:         ctx,
   127  			logIdentity: "ETCD",
   128  			stopTimeout: etcdStopTimeout,
   129  		},
   130  		RuntimeStartOptions:           ETCDStartOptionsForContainerRuntime,
   131  		UseHTTPS:                      false,
   132  		UseTestContainerForNetworking: false,
   133  	}
   134  }
   135  
   136  // DefaultDNSOpt creates default values for DNSOpt
   137  func DefaultDNSOpt(testCtx *TestCtx) *DNSOpt {
   138  	return &DNSOpt{
   139  		Runtime: &ContainerRuntime{
   140  			ctx:         testCtx,
   141  			logIdentity: "DNS server",
   142  			stopTimeout: dnsStopTimeout,
   143  		},
   144  		RuntimeStartOptions: DNSServerStartOptionsForContainerRuntime,
   145  		DomainNameSuffix:    "", // no DNS entries => no common domain name suffix
   146  		HostsConfig:         "", // no DNS entries
   147  	}
   148  }
   149  
   150  // DefaultMicroserviceiOpt creates default values for MicroserviceOpt
   151  func DefaultMicroserviceOpt(testCtx *TestCtx, msName string) *MicroserviceOpt {
   152  	return &MicroserviceOpt{
   153  		Runtime: &ContainerRuntime{
   154  			ctx:         testCtx,
   155  			logIdentity: "Microservice " + msName,
   156  			stopTimeout: msStopTimeout,
   157  		},
   158  		RuntimeStartOptions: MicroserviceStartOptionsForContainerRuntime,
   159  		Name:                msName,
   160  	}
   161  }
   162  
   163  // DefaultAgentOpt creates default values for AgentOpt
   164  func DefaultAgentOpt(testCtx *TestCtx, agentName string) *AgentOpt {
   165  	agentImg := agentImage
   166  	if img := os.Getenv("VPP_AGENT"); img != "" {
   167  		agentImg = img
   168  	}
   169  	grpcConfig := "grpc.conf"
   170  	if val := os.Getenv("GRPC_CONFIG"); val != "" {
   171  		grpcConfig = val
   172  	}
   173  	etcdConfig := "DISABLED"
   174  	if val := os.Getenv("ETCD_CONFIG"); val != "" {
   175  		etcdConfig = val
   176  	}
   177  	opt := &AgentOpt{
   178  		Runtime: &ContainerRuntime{
   179  			ctx:         testCtx,
   180  			logIdentity: "Agent " + agentName,
   181  			stopTimeout: agentStopTimeout,
   182  		},
   183  		RuntimeStartOptions: AgentStartOptionsForContainerRuntime,
   184  		Name:                agentName,
   185  		Image:               agentImg,
   186  		UseEtcd:             false,
   187  		InitialResync:       true,
   188  		Env: []string{
   189  			"INITIAL_LOGLVL=" + logging.DefaultLogger.GetLevel().String(),
   190  			"ETCD_CONFIG=" + etcdConfig,
   191  			"GRPC_CONFIG=" + grpcConfig,
   192  			"DEBUG=" + os.Getenv("DEBUG"),
   193  			"MICROSERVICE_LABEL=" + agentName,
   194  		},
   195  	}
   196  	return opt
   197  }
   198  
   199  // DefaultPingOpts creates default values for PingOpt
   200  func DefaultPingOpts() *PingOpt {
   201  	return &PingOpt{
   202  		AllowedLoss: 49, // by default at least half of the packets should get through
   203  		MaxTimeout:  4,
   204  	}
   205  }
   206  
   207  // WithoutVPPAgent is test setup option disabling vpp-agent setup
   208  func WithoutVPPAgent() SetupOptModifier {
   209  	return func(o *SetupOpt) {
   210  		o.SetupAgent = false
   211  	}
   212  }
   213  
   214  // WithCustomVPPAgent is test setup option using alternative vpp-agent image (customized original vpp-agent)
   215  func WithCustomVPPAgent() SetupOptModifier {
   216  	return func(o *SetupOpt) {
   217  		o.AgentOptMods = append(o.AgentOptMods, func(ao *AgentOpt) {
   218  			ao.Image = "vppagent.test.ligato.io:custom"
   219  		})
   220  	}
   221  }
   222  
   223  // WithEtcd is test setup option enabling etcd setup
   224  func WithEtcd(etcdOptMods ...EtcdOptModifier) SetupOptModifier {
   225  	return func(o *SetupOpt) {
   226  		o.SetupEtcd = true
   227  		o.EtcdOptMods = append(o.EtcdOptMods, etcdOptMods...)
   228  	}
   229  }
   230  
   231  // WithDNSServer is test setup option enabling setup of container serving as dns server
   232  func WithDNSServer(dnsOpts ...DNSOptModifier) SetupOptModifier {
   233  	return func(o *SetupOpt) {
   234  		o.SetupDNSServer = true
   235  	}
   236  }
   237  
   238  // WithoutManualInitialAgentResync is test setup option disabling manual agent resync just after agent setup
   239  func WithoutManualInitialAgentResync() AgentOptModifier {
   240  	return func(o *AgentOpt) {
   241  		o.InitialResync = false
   242  	}
   243  }
   244  
   245  // WithAdditionalAgentCmdParams is test setup option adding additional command line parameters to executing vpp-agent
   246  func WithAdditionalAgentCmdParams(params ...string) AgentOptModifier {
   247  	return func(o *AgentOpt) {
   248  		o.Env = append(o.Env, params...)
   249  	}
   250  }
   251  
   252  // WithZonedStaticEntries is test setup option configuring group of static dns cache entries that belong
   253  // to the same zone (have the same domain name suffix). The static dns cache entries are lines of config file
   254  // in linux /etc/hosts file format.
   255  // Currently supporting only one domain name suffix with static entries (even when DNS server solution supports
   256  // multiple "zones" that each of them can be configured by one file in hosts file format)
   257  func WithZonedStaticEntries(zoneDomainNameSuffix string, staticEntries ...string) DNSOptModifier {
   258  	return func(o *DNSOpt) {
   259  		o.DomainNameSuffix = zoneDomainNameSuffix
   260  		o.HostsConfig = strings.Join(staticEntries, "\n")
   261  	}
   262  }
   263  
   264  // WithPluginConfigArg persists configContent for give VPP-Agent plugin (expecting generic plugin config name)
   265  // and returns argument for VPP-Agent executable to use this plugin configuration file.
   266  func WithPluginConfigArg(ctx *TestCtx, pluginName string, configContent string) string {
   267  	configFilePath := CreateFileOnSharedVolume(ctx, fmt.Sprintf("%v.config", pluginName), configContent)
   268  	return fmt.Sprintf("%v_CONFIG=%v", strings.ToUpper(pluginName), configFilePath)
   269  }
   270  
   271  // FIXME container that will use it can have it mounted in different location as seen by the container where
   272  //  it is created (this works now due to the same mountpoint of shared volume in every container)
   273  
   274  // CreateFileOnSharedVolume persists fileContent to file in mounted shared volume used for sharing file
   275  // between containers. It returns the absolute path to the newly created file as seen by the container
   276  // that creates it.
   277  func CreateFileOnSharedVolume(ctx *TestCtx, simpleFileName string, fileContent string) string {
   278  	// subtest test names can container filepath.Separator
   279  	testName := strings.ReplaceAll(ctx.t.Name(), string(filepath.Separator), "-")
   280  	filePath, err := filepath.Abs(filepath.Join(ctx.ShareDir,
   281  		fmt.Sprintf("e2e-test-%v-%v", testName, simpleFileName)))
   282  	ctx.Expect(err).To(gomega.Not(gomega.HaveOccurred()))
   283  	ctx.Expect(os.WriteFile(filePath, []byte(fileContent), 0777)).To(gomega.Succeed())
   284  
   285  	// TODO register in context and delete in teardown? this doesn't matter
   286  	//  that much because file names contain unique test names so no file collision can happen
   287  	return filePath
   288  }
   289  
   290  // WithMSContainerStartHook is microservice test setup option that will set the microservice container start
   291  // hook that will modify the microservice start options.
   292  func WithMSContainerStartHook(hook func(*docker.CreateContainerOptions)) MicroserviceOptModifier {
   293  	return func(opt *MicroserviceOpt) {
   294  		opt.ContainerOptsHook = hook
   295  	}
   296  }
   297  
   298  // WithEtcdHTTPsConnection is ETCD test setup option that will use HTTPS connection to ETCD (by default it is used
   299  // unsecure HTTP connection)
   300  func WithEtcdHTTPsConnection() EtcdOptModifier {
   301  	return func(o *EtcdOpt) {
   302  		o.UseHTTPS = true
   303  	}
   304  }
   305  
   306  // WithEtcdTestContainerNetworking is ETCD test setup option that will use main Test container for
   307  // networking (by default the ETCD has separate networking)
   308  func WithEtcdTestContainerNetworking() EtcdOptModifier {
   309  	return func(o *EtcdOpt) {
   310  		o.UseTestContainerForNetworking = true
   311  	}
   312  }
   313  
   314  // NewPingOpts create new PingOpt
   315  func NewPingOpts(opts ...PingOptModifier) *PingOpt {
   316  	options := DefaultPingOpts()
   317  	for _, o := range opts {
   318  		o(options)
   319  	}
   320  	return options
   321  }
   322  
   323  func (ping *PingOpt) args() []string {
   324  	var args []string
   325  	if ping.MaxTimeout > 0 {
   326  		args = append(args, "-w", fmt.Sprint(ping.MaxTimeout))
   327  	}
   328  	if ping.Count > 0 {
   329  		args = append(args, "-c", fmt.Sprint(ping.Count))
   330  	}
   331  	if ping.SourceIface != "" {
   332  		args = append(args, "-I", ping.SourceIface)
   333  	}
   334  	return args
   335  }
   336  
   337  // PingWithAllowedLoss sets max allowed packet loss for pinging to be considered successful.
   338  func PingWithAllowedLoss(maxLoss int) PingOptModifier {
   339  	return func(opts *PingOpt) {
   340  		opts.AllowedLoss = maxLoss
   341  	}
   342  }
   343  
   344  // PingWithSourceInterface set source interface for ping packets.
   345  func PingWithSourceInterface(iface string) PingOptModifier {
   346  	return func(opts *PingOpt) {
   347  		opts.SourceIface = iface
   348  	}
   349  }