...

Source file src/github.com/emissary-ingress/emissary/v3/pkg/api/getambassador.io/conversion_test.go

Documentation: github.com/emissary-ingress/emissary/v3/pkg/api/getambassador.io

     1  package getambassadorio_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"os"
     6  	"path"
     7  	"path/filepath"
     8  	"reflect"
     9  	"testing"
    10  
    11  	"github.com/pmezard/go-difflib/difflib"
    12  	"github.com/stretchr/testify/require"
    13  	"k8s.io/apimachinery/pkg/runtime"
    14  	"sigs.k8s.io/yaml"
    15  
    16  	getambassadorio "github.com/emissary-ingress/emissary/v3/pkg/api/getambassador.io"
    17  	"github.com/emissary-ingress/emissary/v3/pkg/api/getambassador.io/v1"
    18  	"github.com/emissary-ingress/emissary/v3/pkg/api/getambassador.io/v2"
    19  	"github.com/emissary-ingress/emissary/v3/pkg/api/getambassador.io/v3alpha1"
    20  	"github.com/emissary-ingress/emissary/v3/pkg/kates"
    21  )
    22  
    23  func marshalNormalized(t *testing.T, typed interface{}) string {
    24  	t.Helper()
    25  
    26  	// First we discard any type information by seralizing-then-deserializing.  This is
    27  	// important so that the order of struct fields doesn't impact the normalized serialization.
    28  
    29  	bs, err := json.Marshal(typed)
    30  	require.NoError(t, err)
    31  
    32  	var untyped interface{}
    33  	err = json.Unmarshal(bs, &untyped)
    34  	require.NoError(t, err)
    35  
    36  	// Now serialize for real.
    37  
    38  	out, err := json.MarshalIndent(untyped, "", "\t")
    39  	require.NoError(t, err)
    40  
    41  	return string(out)
    42  }
    43  
    44  func requireEqualNormalized(t *testing.T, exp, act interface{}) {
    45  	t.Helper()
    46  	expStr := marshalNormalized(t, exp)
    47  	actStr := marshalNormalized(t, act)
    48  	// Do this directly instead of using require.Equal so that the "%q" version doesn't spam
    49  	// stdout.
    50  	if expStr != actStr {
    51  		diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
    52  			A:        difflib.SplitLines(expStr),
    53  			B:        difflib.SplitLines(actStr),
    54  			FromFile: "Expected",
    55  			FromDate: "",
    56  			ToFile:   "Actual",
    57  			ToDate:   "",
    58  			Context:  3,
    59  		})
    60  		t.Errorf("Not equal\n%s", diff)
    61  	}
    62  }
    63  
    64  func TestConvert(t *testing.T) {
    65  	testcases := map[string]map[string]interface{}{
    66  		"authsvc": {
    67  			"getambassador.io/v1":       v1.AuthService{},
    68  			"getambassador.io/v2":       v2.AuthService{},
    69  			"getambassador.io/v3alpha1": v3alpha1.AuthService{},
    70  		},
    71  		"devportals": {
    72  			"getambassador.io/v1":       v1.DevPortal{},
    73  			"getambassador.io/v2":       v2.DevPortal{},
    74  			"getambassador.io/v3alpha1": v3alpha1.DevPortal{},
    75  		},
    76  		"hosts": {
    77  			"getambassador.io/v2":       v2.Host{},
    78  			"getambassador.io/v3alpha1": v3alpha1.Host{},
    79  		},
    80  		"logsvc": {
    81  			"getambassador.io/v1":       v1.LogService{},
    82  			"getambassador.io/v2":       v2.LogService{},
    83  			"getambassador.io/v3alpha1": v3alpha1.LogService{},
    84  		},
    85  		"mappings": {
    86  			"getambassador.io/v1":       v1.Mapping{},
    87  			"getambassador.io/v2":       v2.Mapping{},
    88  			"getambassador.io/v3alpha1": v3alpha1.Mapping{},
    89  		},
    90  		"modules": {
    91  			"getambassador.io/v1":       v1.Module{},
    92  			"getambassador.io/v2":       v2.Module{},
    93  			"getambassador.io/v3alpha1": v3alpha1.Module{},
    94  		},
    95  		"ratelimitsvc": {
    96  			"getambassador.io/v1":       v1.RateLimitService{},
    97  			"getambassador.io/v2":       v2.RateLimitService{},
    98  			"getambassador.io/v3alpha1": v3alpha1.RateLimitService{},
    99  		},
   100  		"tcpmappings": {
   101  			"getambassador.io/v1":       v1.TCPMapping{},
   102  			"getambassador.io/v2":       v2.TCPMapping{},
   103  			"getambassador.io/v3alpha1": v3alpha1.TCPMapping{},
   104  		},
   105  		"tlscontexts": {
   106  			"getambassador.io/v1":       v1.TLSContext{},
   107  			"getambassador.io/v2":       v2.TLSContext{},
   108  			"getambassador.io/v3alpha1": v3alpha1.TLSContext{},
   109  		},
   110  		"tracingsvc": {
   111  			"getambassador.io/v1":       v1.TracingService{},
   112  			"getambassador.io/v2":       v2.TracingService{},
   113  			"getambassador.io/v3alpha1": v3alpha1.TracingService{},
   114  		},
   115  	}
   116  
   117  	scheme := getambassadorio.BuildScheme()
   118  
   119  	v2.MangleAmbassadorID = false
   120  	t.Cleanup(func() {
   121  		v2.MangleAmbassadorID = true
   122  	})
   123  
   124  	t.Run("RoundTrip", func(t *testing.T) {
   125  		t.Parallel()
   126  		for typename := range testcases {
   127  			typename := typename
   128  			t.Run(typename, func(t *testing.T) {
   129  				t.Parallel()
   130  				for mainAPIVersion := range testcases[typename] {
   131  					for throughAPIVersion := range testcases[typename] {
   132  						if mainAPIVersion == throughAPIVersion {
   133  							continue
   134  						}
   135  						mainAPIVersion := mainAPIVersion
   136  						throughAPIVersion := throughAPIVersion
   137  						testname := path.Base(mainAPIVersion) + "_through_" + path.Base(throughAPIVersion)
   138  						t.Run(testname, func(t *testing.T) {
   139  							t.Parallel()
   140  							inBytes, err := os.ReadFile(filepath.Join(path.Base(mainAPIVersion), "testdata", typename+".yaml"))
   141  							require.NoError(t, err)
   142  							inListPtr := reflect.New(reflect.SliceOf(reflect.TypeOf(testcases[typename][mainAPIVersion])))
   143  							require.NoError(t, yaml.Unmarshal(inBytes, inListPtr.Interface()))
   144  							inList := inListPtr.Elem()
   145  							listLen := inList.Len()
   146  
   147  							midList := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(testcases[typename][throughAPIVersion])), listLen, listLen)
   148  							for i := 0; i < listLen; i++ {
   149  								midList.Index(i).FieldByName("TypeMeta").FieldByName("APIVersion").Set(reflect.ValueOf(throughAPIVersion))
   150  								midList.Index(i).FieldByName("TypeMeta").FieldByName("Kind").Set(inList.Index(i).FieldByName("TypeMeta").FieldByName("Kind"))
   151  								require.NoError(t, kates.ConvertObject(scheme, inList.Index(i).Addr().Interface().(runtime.Object), midList.Index(i).Addr().Interface().(runtime.Object)))
   152  							}
   153  
   154  							outList := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(testcases[typename][mainAPIVersion])), listLen, listLen)
   155  							for i := 0; i < listLen; i++ {
   156  								outList.Index(i).FieldByName("TypeMeta").Set(inList.Index(i).FieldByName("TypeMeta"))
   157  								require.NoError(t, kates.ConvertObject(scheme, midList.Index(i).Addr().Interface().(runtime.Object), outList.Index(i).Addr().Interface().(runtime.Object)))
   158  							}
   159  
   160  							requireEqualNormalized(t, inList.Interface(), outList.Interface())
   161  						})
   162  					}
   163  				}
   164  			})
   165  		}
   166  	})
   167  }
   168  
   169  func TestConvertTracingService(t *testing.T) {
   170  
   171  	scheme := getambassadorio.BuildScheme()
   172  
   173  	// v3alpha1 to v2
   174  
   175  	// only custom_tags set
   176  	o := &v2.TracingServiceSpec{}
   177  	err := scheme.Convert(&v3alpha1.TracingServiceSpec{
   178  		AmbassadorID: v3alpha1.AmbassadorID{},
   179  		CustomTags: []v3alpha1.TracingCustomTag{
   180  			{
   181  				Tag: "hola",
   182  				Header: &v3alpha1.TracingCustomTagTypeRequestHeader{
   183  					Name: "hola",
   184  				},
   185  			},
   186  		},
   187  	}, o, nil)
   188  	if err != nil {
   189  		t.Errorf("conversion failed. %v", err)
   190  	}
   191  	if len(o.V3CustomTags) != 1 {
   192  		t.Errorf("got %d; want 1", len(o.V3CustomTags))
   193  	}
   194  	if len(o.TagHeaders) != 0 {
   195  		t.Errorf("got %d; want 0", len(o.TagHeaders))
   196  	}
   197  
   198  	// both custom_tags and tag_headers set
   199  	o2 := &v2.TracingServiceSpec{}
   200  	err = scheme.Convert(&v3alpha1.TracingServiceSpec{
   201  		AmbassadorID:         v3alpha1.AmbassadorID{},
   202  		DeprecatedTagHeaders: []string{"hello"},
   203  		CustomTags: []v3alpha1.TracingCustomTag{
   204  			{
   205  				Tag: "hola",
   206  				Header: &v3alpha1.TracingCustomTagTypeRequestHeader{
   207  					Name: "hola",
   208  				},
   209  			},
   210  			{
   211  				Tag: "env",
   212  				Environment: &v3alpha1.TracingCustomTagTypeEnvironment{
   213  					Name: "env",
   214  				},
   215  			},
   216  		},
   217  	}, o2, nil)
   218  	if err != nil {
   219  		t.Errorf("conversion failed. %v", err)
   220  	}
   221  	if len(o2.V3CustomTags) != 2 {
   222  		t.Errorf("got %d; want 2", len(o2.V3CustomTags))
   223  	}
   224  	if len(o2.TagHeaders) != 0 {
   225  		t.Errorf("got %d; want 0", len(o2.TagHeaders))
   226  	}
   227  
   228  	// only tag_headers set
   229  	o3 := &v2.TracingServiceSpec{}
   230  	err = scheme.Convert(&v3alpha1.TracingServiceSpec{
   231  		AmbassadorID:         v3alpha1.AmbassadorID{},
   232  		DeprecatedTagHeaders: []string{"hello"},
   233  	}, o3, nil)
   234  	if err != nil {
   235  		t.Errorf("conversion failed. %v", err)
   236  	}
   237  	if len(o3.V3CustomTags) != 1 {
   238  		t.Errorf("got %d; want 1", len(o3.V3CustomTags))
   239  	}
   240  	if len(o3.TagHeaders) != 0 {
   241  		t.Errorf("got %d; want 0", len(o3.TagHeaders))
   242  	}
   243  
   244  	// v2 to v3alpha1
   245  
   246  	// only tag_headers set
   247  	out := &v3alpha1.TracingServiceSpec{}
   248  	err = scheme.Convert(&v2.TracingServiceSpec{
   249  		AmbassadorID: v2.AmbassadorID{},
   250  		TagHeaders:   []string{"hola"},
   251  	}, out, nil)
   252  	if err != nil {
   253  		t.Errorf("conversion failed. %v", err)
   254  	}
   255  	if len(out.CustomTags) != 1 {
   256  		t.Errorf("got %d; want 1", len(out.CustomTags))
   257  	}
   258  	if len(out.DeprecatedTagHeaders) != 0 {
   259  		t.Errorf("got %d; want 0", len(out.DeprecatedTagHeaders))
   260  	}
   261  
   262  	// only v3CustomTags set
   263  	out2 := &v3alpha1.TracingServiceSpec{}
   264  	err = scheme.Convert(&v2.TracingServiceSpec{
   265  		AmbassadorID: v2.AmbassadorID{},
   266  		V3CustomTags: []v3alpha1.TracingCustomTag{
   267  			{
   268  				Tag: "hello",
   269  				Header: &v3alpha1.TracingCustomTagTypeRequestHeader{
   270  					Name: "hello",
   271  				},
   272  			},
   273  		},
   274  	}, out2, nil)
   275  	if err != nil {
   276  		t.Errorf("conversion failed. %v", err)
   277  	}
   278  	if len(out2.CustomTags) != 1 {
   279  		t.Errorf("got %d; want 1", len(out2.CustomTags))
   280  	}
   281  	if len(out2.DeprecatedTagHeaders) != 0 {
   282  		t.Errorf("got %d; want 0", len(out2.DeprecatedTagHeaders))
   283  	}
   284  
   285  	// both custom_tags and tag_headers set
   286  	out3 := &v3alpha1.TracingServiceSpec{}
   287  	err = scheme.Convert(&v2.TracingServiceSpec{
   288  		AmbassadorID: v2.AmbassadorID{},
   289  		TagHeaders:   []string{"hola"},
   290  		V3CustomTags: []v3alpha1.TracingCustomTag{
   291  			{
   292  				Tag: "hello",
   293  				Header: &v3alpha1.TracingCustomTagTypeRequestHeader{
   294  					Name: "hello",
   295  				},
   296  			},
   297  			{
   298  				Tag: "hello2",
   299  				Environment: &v3alpha1.TracingCustomTagTypeEnvironment{
   300  					Name: "hello2",
   301  				},
   302  			},
   303  		},
   304  	}, out3, nil)
   305  	if err != nil {
   306  		t.Errorf("conversion failed. %v", err)
   307  	}
   308  	if len(out3.CustomTags) != 2 {
   309  		t.Errorf("got %d; want 2", len(out3.CustomTags))
   310  	}
   311  	if len(out3.DeprecatedTagHeaders) != 0 {
   312  		t.Errorf("got %d; want 0", len(out3.DeprecatedTagHeaders))
   313  	}
   314  
   315  }
   316  

View as plain text