ci: scope scenario parameters instead of whole job (#2578)

* scenario vars

* refined scenario scope

* linux agnhost

* add delete resource

* lint
This commit is contained in:
Mathew Merrick 2024-02-15 08:35:17 -08:00 коммит произвёл GitHub
Родитель 6e66efd7cf
Коммит 9cace8977c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
11 изменённых файлов: 594 добавлений и 147 удалений

Просмотреть файл

@ -120,6 +120,9 @@ func (c *CreateAgnhostStatefulSet) getAgnhostDeployment() *appsv1.StatefulSet {
},
},
},
NodeSelector: map[string]string{
"kubernetes.io/os": "linux",
},
Containers: []v1.Container{
{
Name: c.AgnhostName,

Просмотреть файл

@ -202,6 +202,22 @@ func CreateResource(ctx context.Context, obj runtime.Object, clientset *kubernet
return fmt.Errorf("failed to create/update NetworkPolicy \"%s\" in namespace \"%s\": %w", o.Name, o.Namespace, err)
}
case *v1.Secret:
log.Printf("Creating/Updating Secret \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
client := clientset.CoreV1().Secrets(o.Namespace)
_, err := client.Get(ctx, o.Name, metaV1.GetOptions{})
if errors.IsNotFound(err) {
_, err = client.Create(ctx, o, metaV1.CreateOptions{})
if err != nil {
return fmt.Errorf("failed to create Secret \"%s\" in namespace \"%s\": %w", o.Name, o.Namespace, err)
}
return nil
}
_, err = client.Update(ctx, o, metaV1.UpdateOptions{})
if err != nil {
return fmt.Errorf("failed to create/update Secret \"%s\" in namespace \"%s\": %w", o.Name, o.Namespace, err)
}
default:
return fmt.Errorf("unknown object type: %T, err: %w", obj, ErrUnknownResourceType)
}

Просмотреть файл

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"log"
"time"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
@ -13,10 +14,188 @@ import (
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
var ErrDeleteNilResource = fmt.Errorf("cannot create nil resource")
type ResourceType string
const (
DaemonSet ResourceType = "DaemonSet"
Deployment ResourceType = "Deployment"
StatefulSet ResourceType = "StatefulSet"
Service ResourceType = "Service"
ServiceAccount ResourceType = "ServiceAccount"
Role ResourceType = "Role"
RoleBinding ResourceType = "RoleBinding"
ClusterRole ResourceType = "ClusterRole"
ClusterRoleBinding ResourceType = "ClusterRoleBinding"
ConfigMap ResourceType = "ConfigMap"
NetworkPolicy ResourceType = "NetworkPolicy"
Secret ResourceType = "Secret"
Unknown ResourceType = "Unknown"
)
// Parameters can only be strings, heres to help add guardrails
func TypeString(resourceType ResourceType) string {
ResourceTypes := map[ResourceType]string{
DaemonSet: "DaemonSet",
Deployment: "Deployment",
StatefulSet: "StatefulSet",
Service: "Service",
ServiceAccount: "ServiceAccount",
Role: "Role",
RoleBinding: "RoleBinding",
ClusterRole: "ClusterRole",
ClusterRoleBinding: "ClusterRoleBinding",
ConfigMap: "ConfigMap",
NetworkPolicy: "NetworkPolicy",
Secret: "Secret",
Unknown: "Unknown",
}
str, ok := ResourceTypes[resourceType]
if !ok {
return ResourceTypes[Unknown]
}
return str
}
type DeleteKubernetesResource struct {
ResourceType string // can't use enum, breaks parameter parsing, all must be strings
ResourceName string
ResourceNamespace string
KubeConfigFilePath string
}
func (d *DeleteKubernetesResource) Run() error {
config, err := clientcmd.BuildConfigFromFlags("", d.KubeConfigFilePath)
if err != nil {
return fmt.Errorf("error building kubeconfig: %w", err)
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return fmt.Errorf("error creating Kubernetes client: %w", err)
}
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeoutSeconds*time.Second)
defer cancel()
res := ResourceType(d.ResourceType)
var resource runtime.Object
switch res {
case DaemonSet:
resource = &appsv1.DaemonSet{
ObjectMeta: metaV1.ObjectMeta{
Name: d.ResourceName,
Namespace: d.ResourceNamespace,
},
}
case Deployment:
resource = &appsv1.Deployment{
ObjectMeta: metaV1.ObjectMeta{
Name: d.ResourceName,
Namespace: d.ResourceNamespace,
},
}
case StatefulSet:
resource = &appsv1.StatefulSet{
ObjectMeta: metaV1.ObjectMeta{
Name: d.ResourceName,
Namespace: d.ResourceNamespace,
},
}
case Service:
resource = &v1.Service{
ObjectMeta: metaV1.ObjectMeta{
Name: d.ResourceName,
Namespace: d.ResourceNamespace,
},
}
case ServiceAccount:
resource = &v1.ServiceAccount{
ObjectMeta: metaV1.ObjectMeta{
Name: d.ResourceName,
Namespace: d.ResourceNamespace,
},
}
case Role:
resource = &rbacv1.Role{
ObjectMeta: metaV1.ObjectMeta{
Name: d.ResourceName,
Namespace: d.ResourceNamespace,
},
}
case RoleBinding:
resource = &rbacv1.RoleBinding{
ObjectMeta: metaV1.ObjectMeta{
Name: d.ResourceName,
Namespace: d.ResourceNamespace,
},
}
case ClusterRole:
resource = &rbacv1.ClusterRole{
ObjectMeta: metaV1.ObjectMeta{
Name: d.ResourceName,
},
}
case ClusterRoleBinding:
resource = &rbacv1.ClusterRoleBinding{
ObjectMeta: metaV1.ObjectMeta{
Name: d.ResourceName,
},
}
case ConfigMap:
resource = &v1.ConfigMap{
ObjectMeta: metaV1.ObjectMeta{
Name: d.ResourceName,
Namespace: d.ResourceNamespace,
},
}
case NetworkPolicy:
resource = &networkingv1.NetworkPolicy{
ObjectMeta: metaV1.ObjectMeta{
Name: d.ResourceName,
Namespace: d.ResourceNamespace,
},
}
case Secret:
resource = &v1.Secret{
ObjectMeta: metaV1.ObjectMeta{
Name: d.ResourceName,
Namespace: d.ResourceNamespace,
},
}
case Unknown:
return fmt.Errorf("unknown resource type: %s: %w", d.ResourceType, ErrUnknownResourceType)
default:
return ErrUnknownResourceType
}
err = DeleteResource(ctx, resource, clientset)
if err != nil {
return fmt.Errorf("error deleting resource: %w", err)
}
return nil
}
func (d *DeleteKubernetesResource) Stop() error {
return nil
}
func (d *DeleteKubernetesResource) Prevalidate() error {
restype := ResourceType(d.ResourceType)
if restype == Unknown {
return ErrUnknownResourceType
}
return nil
}
func DeleteResource(ctx context.Context, obj runtime.Object, clientset *kubernetes.Clientset) error { //nolint:gocyclo //this is just boilerplate code
if obj == nil {
return ErrCreateNilResource
@ -36,7 +215,7 @@ func DeleteResource(ctx context.Context, obj runtime.Object, clientset *kubernet
}
case *appsv1.Deployment:
log.Printf("Creating/Updating Deployment \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
log.Printf("Deleting Deployment \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
client := clientset.AppsV1().Deployments(o.Namespace)
err := client.Delete(ctx, o.Name, metaV1.DeleteOptions{})
if err != nil {
@ -48,7 +227,7 @@ func DeleteResource(ctx context.Context, obj runtime.Object, clientset *kubernet
}
case *appsv1.StatefulSet:
log.Printf("Creating/Updating StatefulSet \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
log.Printf("Deleting StatefulSet \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
client := clientset.AppsV1().StatefulSets(o.Namespace)
err := client.Delete(ctx, o.Name, metaV1.DeleteOptions{})
if err != nil {
@ -60,7 +239,7 @@ func DeleteResource(ctx context.Context, obj runtime.Object, clientset *kubernet
}
case *v1.Service:
log.Printf("Creating/Updating Service \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
log.Printf("Deleting Service \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
client := clientset.CoreV1().Services(o.Namespace)
err := client.Delete(ctx, o.Name, metaV1.DeleteOptions{})
if err != nil {
@ -72,7 +251,7 @@ func DeleteResource(ctx context.Context, obj runtime.Object, clientset *kubernet
}
case *v1.ServiceAccount:
log.Printf("Creating/Updating ServiceAccount \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
log.Printf("Deleting ServiceAccount \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
client := clientset.CoreV1().ServiceAccounts(o.Namespace)
err := client.Delete(ctx, o.Name, metaV1.DeleteOptions{})
if err != nil {
@ -84,7 +263,7 @@ func DeleteResource(ctx context.Context, obj runtime.Object, clientset *kubernet
}
case *rbacv1.Role:
log.Printf("Creating/Updating Role \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
log.Printf("Deleting Role \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
client := clientset.RbacV1().Roles(o.Namespace)
err := client.Delete(ctx, o.Name, metaV1.DeleteOptions{})
if err != nil {
@ -96,7 +275,7 @@ func DeleteResource(ctx context.Context, obj runtime.Object, clientset *kubernet
}
case *rbacv1.RoleBinding:
log.Printf("Creating/Updating RoleBinding \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
log.Printf("Deleting RoleBinding \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
client := clientset.RbacV1().RoleBindings(o.Namespace)
err := client.Delete(ctx, o.Name, metaV1.DeleteOptions{})
if err != nil {
@ -108,7 +287,7 @@ func DeleteResource(ctx context.Context, obj runtime.Object, clientset *kubernet
}
case *rbacv1.ClusterRole:
log.Printf("Creating/Updating ClusterRole \"%s\"...\n", o.Name)
log.Printf("Deleting ClusterRole \"%s\"...\n", o.Name)
client := clientset.RbacV1().ClusterRoles()
err := client.Delete(ctx, o.Name, metaV1.DeleteOptions{})
if err != nil {
@ -120,7 +299,7 @@ func DeleteResource(ctx context.Context, obj runtime.Object, clientset *kubernet
}
case *rbacv1.ClusterRoleBinding:
log.Printf("Creating/Updating ClusterRoleBinding \"%s\"...\n", o.Name)
log.Printf("Deleting ClusterRoleBinding \"%s\"...\n", o.Name)
client := clientset.RbacV1().ClusterRoleBindings()
err := client.Delete(ctx, o.Name, metaV1.DeleteOptions{})
if err != nil {
@ -132,7 +311,7 @@ func DeleteResource(ctx context.Context, obj runtime.Object, clientset *kubernet
}
case *v1.ConfigMap:
log.Printf("Creating/Updating ConfigMap \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
log.Printf("Deleting ConfigMap \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
client := clientset.CoreV1().ConfigMaps(o.Namespace)
err := client.Delete(ctx, o.Name, metaV1.DeleteOptions{})
if err != nil {
@ -144,7 +323,7 @@ func DeleteResource(ctx context.Context, obj runtime.Object, clientset *kubernet
}
case *networkingv1.NetworkPolicy:
log.Printf("Creating/Updating NetworkPolicy \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
log.Printf("Deleting NetworkPolicy \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
client := clientset.NetworkingV1().NetworkPolicies(o.Namespace)
err := client.Delete(ctx, o.Name, metaV1.DeleteOptions{})
if err != nil {
@ -155,6 +334,18 @@ func DeleteResource(ctx context.Context, obj runtime.Object, clientset *kubernet
return fmt.Errorf("failed to delete NetworkPolicy \"%s\" in namespace \"%s\": %w", o.Name, o.Namespace, err)
}
case *v1.Secret:
log.Printf("Deleting Secret \"%s\" in namespace \"%s\"...\n", o.Name, o.Namespace)
client := clientset.CoreV1().Secrets(o.Namespace)
err := client.Delete(ctx, o.Name, metaV1.DeleteOptions{})
if err != nil {
if errors.IsNotFound(err) {
log.Printf("Secret \"%s\" in namespace \"%s\" does not exist\n", o.Name, o.Namespace)
return nil
}
return fmt.Errorf("failed to delete Secret \"%s\" in namespace \"%s\": %w", o.Name, o.Namespace, err)
}
default:
return fmt.Errorf("unknown object type: %T, err: %w", obj, ErrUnknownResourceType)
}

Просмотреть файл

@ -72,7 +72,6 @@ func (p *PortForward) Run() error {
}
portForwardFn := func() error {
// if we have a pod name (likely from affinity above), use it, otherwise use label selector
opts := k8s.PortForwardingOpts{
Namespace: p.Namespace,

Просмотреть файл

@ -6,27 +6,25 @@ import (
"sync"
"testing"
"time"
"github.com/Azure/azure-container-networking/test/e2e/framework/types"
)
func TestFramework(t *testing.T) {
job := types.NewJob("Validate that drop metrics are present in the prometheus endpoint")
runner := types.NewRunner(t, job)
job := NewJob("Validate that drop metrics are present in the prometheus endpoint")
runner := NewRunner(t, job)
defer runner.Run()
job.AddStep(&TestBackground{
CounterName: "Example Counter",
}, &types.StepOptions{
}, &StepOptions{
ExpectError: false,
RunInBackgroundWithID: "TestStep",
})
job.AddStep(&types.Sleep{
job.AddStep(&Sleep{
Duration: 1 * time.Second,
}, nil)
job.AddStep(&types.Stop{
job.AddStep(&Stop{
BackgroundID: "TestStep",
}, nil)
}

Просмотреть файл

@ -1,6 +1,7 @@
package types
import (
"errors"
"fmt"
"log"
"reflect"
@ -15,14 +16,17 @@ var (
ErrOrphanSteps = fmt.Errorf("background steps with no corresponding stop")
ErrCannotStopStep = fmt.Errorf("cannot stop step")
ErrMissingBackroundID = fmt.Errorf("missing background id")
ErrNoValue = fmt.Errorf("empty parameter not found saved in values")
ErrEmptyScenarioName = fmt.Errorf("scenario name is empty")
)
// A Job is a logical grouping of steps, options and values
type Job struct {
Values *JobValues
values *JobValues
Description string
Steps []*StepWrapper
BackgroundSteps map[string]*StepWrapper
Scenarios map[*StepWrapper]*Scenario
}
// A StepWrapper is a coupling of a step and it's options
@ -34,17 +38,41 @@ type StepWrapper struct {
// A Scenario is a logical grouping of steps, used to describe a scenario such as "test drop metrics"
// which will require port forwarding, exec'ing, scraping, etc.
type Scenario struct {
Steps []*StepWrapper
name string
steps []*StepWrapper
values *JobValues
}
func responseDivider(jobname string) {
totalWidth := 100
func NewScenario(name string, steps ...*StepWrapper) *Scenario {
if name == "" {
log.Printf("scenario name is empty")
}
return &Scenario{
name: name,
steps: steps,
values: &JobValues{kv: make(map[string]string)},
}
}
func (j *Job) GetPrettyStepName(step *StepWrapper) string {
prettyname := reflect.TypeOf(step.Step).Elem().Name()
if j.Scenarios[step] != nil {
prettyname = fmt.Sprintf("%s (scenario: %s)", prettyname, j.Scenarios[step].name)
}
return prettyname
}
func (j *Job) responseDivider(wrapper *StepWrapper) {
totalWidth := 125
start := 20
i := 0
for ; i < start; i++ {
fmt.Print("#")
}
mid := fmt.Sprintf(" %s ", jobname)
mid := fmt.Sprintf(" %s ", j.GetPrettyStepName(wrapper))
fmt.Print(mid)
for ; i < totalWidth-(start+len(mid)); i++ {
fmt.Print("#")
@ -54,17 +82,19 @@ func responseDivider(jobname string) {
func NewJob(description string) *Job {
return &Job{
Values: &JobValues{
values: &JobValues{
kv: make(map[string]string),
},
BackgroundSteps: make(map[string]*StepWrapper),
Scenarios: make(map[*StepWrapper]*Scenario),
Description: description,
}
}
func (j *Job) AddScenario(scenario *Scenario) {
for _, step := range scenario.Steps {
j.AddStep(step.Step, step.Opts)
for i, step := range scenario.steps {
j.Steps = append(j.Steps, step)
j.Scenarios[scenario.steps[i]] = scenario
}
}
@ -76,6 +106,56 @@ func (j *Job) AddStep(step Step, opts *StepOptions) {
j.Steps = append(j.Steps, stepw)
}
func (j *Job) GetValue(stepw *StepWrapper, key string) (string, bool) {
// if step exists in a scenario, use the scenario's values
// if the value isn't in the scenario's values, get the root job's value
if scenario, exists := j.Scenarios[stepw]; exists {
if scenario.values.Contains(key) {
return scenario.values.Get(key), true
}
}
if j.values.Contains(key) {
return j.values.Get(key), true
}
return "", false
}
// SetGetValues is used when we want to save parameters to job, and also check if
// the parameter exists in the scenario's or top level values
func (j *Job) SetGetValues(stepw *StepWrapper, key, value string) (string, error) {
// if top level step parameter is set, and scenario step is not, inherit
// if top level step parameter is not set, and scenario step is, use scenario step
// if top level step parameter is set, and scenario step is set, warn and use scenario step
// check if scenario exists, if it does, check if the value is in the scenario's values
if scenario, exists := j.Scenarios[stepw]; exists {
scenarioValue, err := scenario.values.SetGet(key, value)
if err != nil && !errors.Is(err, ErrEmptyValue) {
return "", err
}
if scenarioValue != "" {
return scenarioValue, nil
}
}
return j.values.SetGet(key, value)
}
// GetValues is used when we want to skip saving parameters to job, but also check if
// the parameter exists in the scenario's or top level values
func (j *Job) GetValues(stepw *StepWrapper, key string) string {
// check if scenario exists, if it does, check if the value is in the scenario's values
if scenario, exists := j.Scenarios[stepw]; exists {
scenarioValue := scenario.values.Get(key)
if scenarioValue != "" {
return scenarioValue
}
}
return j.values.Get(key)
}
func (j *Job) Run() error {
if j.Description == "" {
return ErrEmptyDescription
@ -95,7 +175,7 @@ func (j *Job) Run() error {
}
for _, wrapper := range j.Steps {
responseDivider(reflect.TypeOf(wrapper.Step).Elem().Name())
j.responseDivider(wrapper)
err := wrapper.Step.Run()
if wrapper.Opts.ExpectError && err == nil {
return fmt.Errorf("expected error from step %s but got nil: %w", reflect.TypeOf(wrapper.Step).Elem().Name(), ErrNilError)
@ -137,10 +217,10 @@ func (j *Job) validateBackgroundSteps() error {
}
if j.BackgroundSteps[s.BackgroundID] == nil {
return fmt.Errorf("cannot stop step %s, as it won't be started by this time; %w", s.BackgroundID, ErrCannotStopStep)
return fmt.Errorf("cannot stop step \"%s\", as it won't be started by this time; %w", s.BackgroundID, ErrCannotStopStep)
}
if stopped := stoppedBackgroundSteps[s.BackgroundID]; stopped {
return fmt.Errorf("cannot stop step %s, as it has already been stopped; %w", s.BackgroundID, ErrCannotStopStep)
return fmt.Errorf("cannot stop step \"%s\", as it has already been stopped; %w", s.BackgroundID, ErrCannotStopStep)
}
// track for later on if the stop step is called
@ -152,7 +232,7 @@ func (j *Job) validateBackgroundSteps() error {
default:
if stepw.Opts.RunInBackgroundWithID != "" {
if _, exists := j.BackgroundSteps[stepw.Opts.RunInBackgroundWithID]; exists {
log.Fatalf("step with id %s already exists", stepw.Opts.RunInBackgroundWithID)
log.Fatalf("step with id \"%s\" already exists", stepw.Opts.RunInBackgroundWithID)
}
j.BackgroundSteps[stepw.Opts.RunInBackgroundWithID] = stepw
stoppedBackgroundSteps[stepw.Opts.RunInBackgroundWithID] = false
@ -162,23 +242,22 @@ func (j *Job) validateBackgroundSteps() error {
for stepName, stopped := range stoppedBackgroundSteps {
if !stopped {
return fmt.Errorf("step %s was not stopped; %w", stepName, ErrOrphanSteps)
return fmt.Errorf("step \"%s\" was not stopped; %w", stepName, ErrOrphanSteps)
}
}
return nil
}
func (j *Job) validateStep(stepw *StepWrapper) error {
stepName := reflect.TypeOf(stepw.Step).Elem().Name()
val := reflect.ValueOf(stepw.Step).Elem()
func (j *Job) validateStep(step *StepWrapper) error {
val := reflect.ValueOf(step.Step).Elem()
// set default options if none are provided
if stepw.Opts == nil {
stepw.Opts = &DefaultOpts
if step.Opts == nil {
step.Opts = &DefaultOpts
}
switch stepw.Step.(type) {
switch step.Step.(type) {
case *Stop:
// don't validate stop steps
return nil
@ -199,30 +278,41 @@ func (j *Job) validateStep(stepw *StepWrapper) error {
if k == reflect.String {
parameter := val.Type().Field(i).Name
value := val.Field(i).Interface().(string)
storedValue := j.Values.Get(parameter)
passedvalue := val.Field(i).Interface().(string)
if storedValue == "" {
// if top level step parameter is set, and scenario step is not, inherit
// if top level step parameter is not set, and scenario step is, use scenario step
// if top level step parameter is set, and scenario step is set, warn and use scenario step
switch {
case stepw.Opts.SkipSavingParamatersToJob:
continue
case value != "":
fmt.Printf("\"%s\" setting parameter \"%s\" in job context to \"%s\"\n", stepName, parameter, value)
j.Values.Set(parameter, value)
default:
return fmt.Errorf("missing parameter \"%s\" for step \"%s\": %w", parameter, stepName, ErrMissingParameter)
}
continue
var err error
var value string
if step.Opts.SkipSavingParamatersToJob {
retrievedvalue := j.GetValues(step, parameter)
// if the value is already set, and it's not the same as the one we're trying to set, error
if retrievedvalue != "" && passedvalue != "" && retrievedvalue != passedvalue {
return fmt.Errorf("parameter \"%s\" was set as \"%s\", but was already saved as \"%s\"; %w", parameter, retrievedvalue, passedvalue, ErrParameterAlreadySet)
}
if value != "" {
return fmt.Errorf("parameter %s for step %s is already set from previous step: %w", parameter, stepName, ErrParameterAlreadySet)
if passedvalue == "" {
if retrievedvalue == "" {
return fmt.Errorf("parameter \"%s\" is empty in step \"%s\"; %w", parameter, j.GetPrettyStepName(step), ErrNoValue)
}
value = retrievedvalue
} else {
value = passedvalue
}
} else {
value, err = j.SetGetValues(step, parameter, passedvalue)
if err != nil {
return fmt.Errorf("error setting parameter \"%s\": in step \"%s\": %w", parameter, j.GetPrettyStepName(step), err)
}
}
// don't use log format since this is technically preexecution and easier to read
fmt.Println(stepName, "using previously stored value for parameter", parameter, "set as", j.Values.Get(parameter))
val.Field(i).SetString(storedValue)
fmt.Printf("%s setting stored value for parameter [%s] set as [%s]\n", j.GetPrettyStepName(step), parameter, value)
val.Field(i).SetString(value)
}
}
}

Просмотреть файл

@ -1,6 +1,14 @@
package types
import "sync"
import (
"fmt"
"sync"
)
var (
ErrValueAlreadySet = fmt.Errorf("parameter already set in values")
ErrEmptyValue = fmt.Errorf("empty parameter not found in values")
)
type JobValues struct {
RWLock sync.RWMutex
@ -26,8 +34,21 @@ func (j *JobValues) Get(key string) string {
return j.kv[key]
}
func (j *JobValues) Set(key, value string) {
func (j *JobValues) SetGet(key, value string) (string, error) {
j.RWLock.Lock()
defer j.RWLock.Unlock()
_, ok := j.kv[key]
switch {
case !ok && value != "":
j.kv[key] = value
return value, nil
case ok && value == "":
return j.kv[key], nil
case ok && value != "":
return "", ErrValueAlreadySet
}
return "", ErrEmptyValue
}

Просмотреть файл

@ -0,0 +1,126 @@
package types
import (
"fmt"
"testing"
)
// Test against a BYO cluster with Cilium and Hubble enabled,
// create a pod with a deny all network policy and validate
// that the drop metrics are present in the prometheus endpoint
func TestScenarioValues(t *testing.T) {
job := NewJob("Validate that drop metrics are present in the prometheus endpoint")
runner := NewRunner(t, job)
defer runner.Run()
// Add top level step
job.AddStep(&DummyStep{
Parameter1: "Top Level Step 1",
Parameter2: "Top Level Step 2",
}, nil)
// Add scenario to ensure that the parameters are set correctly
// and inherited without overriding
job.AddScenario(NewDummyScenario())
job.AddStep(&DummyStep{}, nil)
}
// Test against a BYO cluster with Cilium and Hubble enabled,
// create a pod with a deny all network policy and validate
// that the drop metrics are present in the prometheus endpoint
func TestScenarioValuesWithSkip(t *testing.T) {
job := NewJob("Validate that drop metrics are present in the prometheus endpoint")
runner := NewRunner(t, job)
defer runner.Run()
// Add top level step
job.AddStep(&DummyStep{
Parameter1: "Top Level Step 1",
Parameter2: "Top Level Step 2",
}, &StepOptions{
SkipSavingParamatersToJob: true,
})
// top level step skips saving parameters, so we should error here
// that parameters are missing
job.AddScenario(NewDummyScenario())
job.AddStep(&DummyStep{
Parameter1: "Other Level Step 1",
Parameter2: "Other Level Step 2",
}, nil)
}
func TestScenarioValuesWithScenarioSkip(t *testing.T) {
job := NewJob("Validate that drop metrics are present in the prometheus endpoint")
runner := NewRunner(t, job)
defer runner.Run()
// Add top level step
job.AddStep(&DummyStep{
Parameter1: "Kubeconfig path 1",
Parameter2: "Kubeconfig path 2",
}, nil)
// top level step skips saving parameters, so we should error here
// that parameters are missing
job.AddScenario(NewDummyScenarioWithSkipSave())
// Add top level step
job.AddStep(&DummyStep{}, nil)
}
func NewDummyScenario() *Scenario {
return NewScenario("Dummy Scenario",
&StepWrapper{
Step: &DummyStep{
Parameter1: "Something in Scenario 1",
Parameter2: "Something in Scenario 1",
},
},
)
}
func NewDummyScenario2() *Scenario {
return NewScenario("Dummy Scenario",
&StepWrapper{
Step: &DummyStep{
Parameter1: "Something 2 in Scenario 1",
Parameter2: "Something 2 in Scenario 1",
},
},
)
}
func NewDummyScenarioWithSkipSave() *Scenario {
return NewScenario("Dummy Scenario",
&StepWrapper{
Step: &DummyStep{
Parameter1: "",
Parameter2: "",
}, Opts: &StepOptions{
SkipSavingParamatersToJob: true,
},
},
)
}
type DummyStep struct {
Parameter1 string
Parameter2 string
}
func (d *DummyStep) Run() error {
fmt.Printf("Running DummyStep with parameter 1 as: %s\n", d.Parameter1)
fmt.Printf("Running DummyStep with parameter 2 as: %s\n", d.Parameter2)
return nil
}
func (d *DummyStep) Stop() error {
return nil
}
func (d *DummyStep) Prevalidate() error {
return nil
}

Просмотреть файл

@ -7,8 +7,7 @@ import (
// todo: once AMA is rolled out
func ValidateAMATargets() *types.Scenario {
return &types.Scenario{
Steps: []*types.StepWrapper{
steps := []*types.StepWrapper{
{
Step: &k8s.PortForward{
Namespace: "kube-system",
@ -30,6 +29,10 @@ func ValidateAMATargets() *types.Scenario {
BackgroundID: "validate-ama-targets",
},
},
},
}
return types.NewScenario(
"Validate that drop metrics are present in the prometheus endpoint",
steps...,
)
}

Просмотреть файл

@ -16,8 +16,8 @@ const (
)
func ValidateDropMetric() *types.Scenario {
return &types.Scenario{
Steps: []*types.StepWrapper{
Name := "Validate that drop metrics are present in the prometheus endpoint"
Steps := []*types.StepWrapper{
{
Step: &k8s.CreateKapingerDeployment{
KapingerNamespace: "kube-system",
@ -89,6 +89,6 @@ func ValidateDropMetric() *types.Scenario {
BackgroundID: "hubble-drop-port-forward",
},
},
},
}
return types.NewScenario(Name, Steps...)
}

Просмотреть файл

@ -4,13 +4,13 @@ import "github.com/Azure/azure-container-networking/test/e2e/framework/types"
// todo: once AMA is rolled out
func ValidateAMATargets() *types.Scenario {
return &types.Scenario{
Steps: []*types.StepWrapper{
name := "Validate that flow metrics are present in the prometheus endpoint"
steps := []*types.StepWrapper{
{
Step: &ValidateHubbleFlowMetric{
LocalPort: "9090",
},
},
},
}
return types.NewScenario(name, steps...)
}