func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) { //t.Logf("codec: %#v", codec) printer := spew.ConfigState{DisableMethods: true} name := reflect.TypeOf(item).Elem().Name() data, err := runtime.Encode(codec, item) if err != nil { t.Errorf("%v: %v (%s)", name, err, printer.Sprintf("%#v", item)) return } obj2, err := runtime.Decode(codec, data) if err != nil { t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), printer.Sprintf("%#v", item)) return } if !api.Semantic.DeepEqual(item, obj2) { t.Errorf("\n1: %v: diff: %v\nCodec: %v\nSource:\n\n%#v\n\nEncoded:\n\n%s\n\nFinal:\n\n%#v", name, diff.ObjectGoPrintDiff(item, obj2), codec, printer.Sprintf("%#v", item), string(data), printer.Sprintf("%#v", obj2)) return } obj3 := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) if err := runtime.DecodeInto(codec, data, obj3); err != nil { t.Errorf("2: %v: %v", name, err) return } if !api.Semantic.DeepEqual(item, obj3) { t.Errorf("3: %v: diff: %v\nCodec: %v", name, diff.ObjectDiff(item, obj3), codec) return } }
func (r *subjectAccessTest) runTest(t *testing.T) { storage := NewREST(subjectaccessreview.NewRegistry(subjectaccessreview.NewREST(r.authorizer))) expectedResponse := &authorizationapi.SubjectAccessReviewResponse{ Namespace: r.reviewRequest.Action.Namespace, Allowed: r.authorizer.allowed, Reason: r.authorizer.reason, } expectedAttributes := authorizer.ToDefaultAuthorizationAttributes(r.reviewRequest.Action) ctx := kapi.WithNamespace(kapi.NewContext(), r.reviewRequest.Action.Namespace) if r.requestingUser != nil { ctx = kapi.WithUser(ctx, r.requestingUser) } obj, err := storage.Create(ctx, r.reviewRequest) if err != nil && len(r.authorizer.err) == 0 { t.Fatalf("unexpected error: %v", err) } if len(r.authorizer.err) != 0 { if err == nil { t.Fatalf("unexpected non-error: %v", err) } if e, a := r.authorizer.err, err.Error(); e != a { t.Fatalf("expected %v, got %v", e, a) } return } switch obj.(type) { case *authorizationapi.SubjectAccessReviewResponse: if !reflect.DeepEqual(expectedResponse, obj) { t.Errorf("diff %v", diff.ObjectGoPrintDiff(expectedResponse, obj)) } case nil: if len(r.authorizer.err) == 0 { t.Fatal("unexpected nil object") } default: t.Errorf("Unexpected obj type: %v", obj) } if !reflect.DeepEqual(expectedAttributes, r.authorizer.actualAttributes) { t.Errorf("diff %v", diff.ObjectGoPrintDiff(expectedAttributes, r.authorizer.actualAttributes)) } if !reflect.DeepEqual(r.expectedUserInfo, r.authorizer.actualUserInfo) { t.Errorf("diff %v", diff.ObjectGoPrintDiff(r.expectedUserInfo, r.authorizer.actualUserInfo)) } }
func (r *subjectAccessTest) runTest(t *testing.T) { storage := REST{r.authorizer} expectedResponse := &authorizationapi.SubjectAccessReviewResponse{ Namespace: r.reviewRequest.Action.Namespace, Allowed: r.authorizer.allowed, Reason: r.authorizer.reason, EvaluationError: r.authorizer.err, } expectedAttributes := authorizer.ToDefaultAuthorizationAttributes(r.reviewRequest.Action) ctx := kapi.WithNamespace(kapi.NewContext(), kapi.NamespaceAll) if r.requestingUser != nil { ctx = kapi.WithUser(ctx, r.requestingUser) } obj, err := storage.Create(ctx, r.reviewRequest) switch { case err == nil && len(r.expectedError) == 0: case err == nil && len(r.expectedError) != 0: t.Fatalf("missing expected error: %v", r.expectedError) case err != nil && len(r.expectedError) == 0: t.Fatalf("unexpected error: %v", err) case err != nil && len(r.expectedError) == 0 && err.Error() != r.expectedError: t.Fatalf("unexpected error: %v", r.expectedError) } if len(r.expectedError) > 0 { return } switch obj.(type) { case *authorizationapi.SubjectAccessReviewResponse: if !reflect.DeepEqual(expectedResponse, obj) { t.Errorf("diff %v", diff.ObjectGoPrintDiff(expectedResponse, obj)) } default: t.Errorf("Unexpected obj type: %v", obj) } if !reflect.DeepEqual(expectedAttributes, r.authorizer.actualAttributes) { t.Errorf("diff %v", diff.ObjectGoPrintDiff(expectedAttributes, r.authorizer.actualAttributes)) } if !reflect.DeepEqual(r.expectedUserInfo, r.authorizer.actualUserInfo) { t.Errorf("diff %v", diff.ObjectGoPrintDiff(r.expectedUserInfo, r.authorizer.actualUserInfo)) } }
func validateEvent(messagePrefix string, actualEvent *api.Event, expectedEvent *api.Event, t *testing.T) (*api.Event, error) { recvEvent := *actualEvent expectCompression := expectedEvent.Count > 1 t.Logf("%v - expectedEvent.Count is %d\n", messagePrefix, expectedEvent.Count) // Just check that the timestamp was set. if recvEvent.FirstTimestamp.IsZero() || recvEvent.LastTimestamp.IsZero() { t.Errorf("%v - timestamp wasn't set: %#v", messagePrefix, recvEvent) } actualFirstTimestamp := recvEvent.FirstTimestamp actualLastTimestamp := recvEvent.LastTimestamp if actualFirstTimestamp.Equal(actualLastTimestamp) { if expectCompression { t.Errorf("%v - FirstTimestamp (%q) and LastTimestamp (%q) must be different to indicate event compression happened, but were the same. Actual Event: %#v", messagePrefix, actualFirstTimestamp, actualLastTimestamp, recvEvent) } } else { if expectedEvent.Count == 1 { t.Errorf("%v - FirstTimestamp (%q) and LastTimestamp (%q) must be equal to indicate only one occurrence of the event, but were different. Actual Event: %#v", messagePrefix, actualFirstTimestamp, actualLastTimestamp, recvEvent) } } // Temp clear time stamps for comparison because actual values don't matter for comparison recvEvent.FirstTimestamp = expectedEvent.FirstTimestamp recvEvent.LastTimestamp = expectedEvent.LastTimestamp // Check that name has the right prefix. if n, en := recvEvent.Name, expectedEvent.Name; !strings.HasPrefix(n, en) { t.Errorf("%v - Name '%v' does not contain prefix '%v'", messagePrefix, n, en) } recvEvent.Name = expectedEvent.Name if e, a := expectedEvent, &recvEvent; !reflect.DeepEqual(e, a) { t.Errorf("%v - diff: %s", messagePrefix, diff.ObjectGoPrintDiff(e, a)) } recvEvent.FirstTimestamp = actualFirstTimestamp recvEvent.LastTimestamp = actualLastTimestamp return actualEvent, nil }
func (r *resourceAccessTest) runTest(t *testing.T) { storage := REST{r.authorizer} expectedResponse := &authorizationapi.ResourceAccessReviewResponse{ Namespace: r.reviewRequest.Action.Namespace, Users: r.authorizer.users, Groups: r.authorizer.groups, } expectedAttributes := authorizer.ToDefaultAuthorizationAttributes(r.reviewRequest.Action) ctx := kapi.WithNamespace(kapi.NewContext(), kapi.NamespaceAll) obj, err := storage.Create(ctx, r.reviewRequest) if err != nil && len(r.authorizer.err) == 0 { t.Fatalf("unexpected error: %v", err) } if len(r.authorizer.err) != 0 { if err == nil { t.Fatalf("unexpected non-error: %v", err) } if e, a := r.authorizer.err, err.Error(); e != a { t.Fatalf("expected %v, got %v", e, a) } return } switch obj.(type) { case *authorizationapi.ResourceAccessReviewResponse: if !reflect.DeepEqual(expectedResponse, obj) { t.Errorf("diff %v", diff.ObjectGoPrintDiff(expectedResponse, obj)) } case nil: if len(r.authorizer.err) == 0 { t.Fatal("unexpected nil object") } default: t.Errorf("Unexpected obj type: %v", obj) } if !reflect.DeepEqual(expectedAttributes, r.authorizer.actualAttributes) { t.Errorf("diff %v", diff.ObjectGoPrintDiff(expectedAttributes, r.authorizer.actualAttributes)) } }
func TestRepositoryBucketCopy(t *testing.T) { now := time.Now() clock := util.NewFakeClock(now) ttl5m := time.Minute * 5 for _, tc := range []struct { name string entries []bucketEntry expectedRepos []string }{ { name: "no entry", expectedRepos: []string{}, }, { name: "one stale entry", entries: []bucketEntry{ { repository: "1", }, }, expectedRepos: []string{}, }, { name: "two entries", entries: []bucketEntry{ { repository: "a", evictOn: now.Add(ttl5m), }, { repository: "b", evictOn: now.Add(ttl5m), }, }, expectedRepos: []string{"a", "b"}, }, } { b := repositoryBucket{ clock: clock, list: tc.entries, } result := b.Copy() if !reflect.DeepEqual(result, tc.expectedRepos) { t.Errorf("[%s] got unexpected repo list: %s", tc.name, diff.ObjectGoPrintDiff(result, tc.expectedRepos)) } } }
func TestStrategyPrepareForCreate(t *testing.T) { ctx := kapi.NewDefaultContext() original := api.Image{ ObjectMeta: kapi.ObjectMeta{ Name: "image", }, } seed := int64(2703387474910584091) //rand.Int63() fuzzed := fuzzImage(t, &original, seed) obj, err := kapi.Scheme.DeepCopy(fuzzed) if err != nil { t.Fatalf("faild to deep copy fuzzed image: %v", err) } image := obj.(*api.Image) if len(image.Signatures) == 0 { t.Fatalf("fuzzifier failed to generate signatures") } Strategy.PrepareForCreate(ctx, image) if len(image.Signatures) != len(fuzzed.Signatures) { t.Errorf("unexpected number of signatures: %d != %d", len(image.Signatures), len(fuzzed.Signatures)) } for i, sig := range image.Signatures { vi := reflect.ValueOf(&sig).Elem() vf := reflect.ValueOf(&fuzzed.Signatures[i]).Elem() typeOfT := vf.Type() for j := 0; j < vf.NumField(); j++ { iField := vi.Field(j) fField := vf.Field(j) typeOfF := fField.Type() switch typeOfT.Field(j).Name { case "Content", "Type", "TypeMeta", "ObjectMeta": if !reflect.DeepEqual(iField.Interface(), fField.Interface()) { t.Errorf("%s field should not differ: %s", typeOfT.Field(j).Name, diff.ObjectGoPrintDiff(iField.Interface(), fField.Interface())) } default: if !reflect.DeepEqual(iField.Interface(), reflect.Zero(typeOfF).Interface()) { t.Errorf("expected Signatures.%s to be unset, not %#+v", typeOfF.Field(j).Name, iField.Interface()) } } } } }
func TestAPIServerDefaults(t *testing.T) { defaults := apiserveroptions.NewAPIServer() // This is a snapshot of the default config // If the default changes (new fields are added, or default values change), we want to know // Once we've reacted to the changes appropriately in BuildKubernetesMasterConfig(), update this expected default to match the new upstream defaults expectedDefaults := &apiserveroptions.APIServer{ ServerRunOptions: &genericapiserver.ServerRunOptions{ BindAddress: net.ParseIP("0.0.0.0"), CertDirectory: "/var/run/kubernetes", InsecureBindAddress: net.ParseIP("127.0.0.1"), InsecurePort: 8080, LongRunningRequestRE: "(/|^)((watch|proxy)(/|$)|(logs?|portforward|exec|attach)/?$)", MaxRequestsInFlight: 400, SecurePort: 6443, APIGroupPrefix: "/apis", APIPrefix: "/api", EnableLogsSupport: true, EnableProfiling: true, EnableWatchCache: true, MinRequestTimeout: 1800, RuntimeConfig: utilconfig.ConfigurationMap{}, StorageVersions: registered.AllPreferredGroupVersions(), MasterCount: 1, DefaultStorageVersions: registered.AllPreferredGroupVersions(), StorageConfig: storagebackend.Config{ Prefix: "/registry", DeserializationCacheSize: genericapiserver.DefaultDeserializationCacheSize, }, }, DefaultStorageMediaType: "application/json", AdmissionControl: "AlwaysAdmit", AuthorizationMode: "AlwaysAllow", DeleteCollectionWorkers: 1, EventTTL: 1 * time.Hour, MasterServiceNamespace: "default", KubeletConfig: kubeletclient.KubeletClientConfig{ Port: 10250, EnableHttps: true, HTTPTimeout: time.Duration(5) * time.Second, }, } if !reflect.DeepEqual(defaults, expectedDefaults) { t.Logf("expected defaults, actual defaults: \n%s", diff.ObjectGoPrintDiff(expectedDefaults, defaults)) t.Errorf("Got different defaults than expected, adjust in BuildKubernetesMasterConfig and update expectedDefaults") } }
func TestProtobufRoundTrip(t *testing.T) { obj := &v1.Pod{} apitesting.FuzzerFor(t, v1.SchemeGroupVersion, rand.NewSource(benchmarkSeed)).Fuzz(obj) // InitContainers are turned into annotations by conversion. obj.Spec.InitContainers = nil obj.Status.InitContainerStatuses = nil data, err := obj.Marshal() if err != nil { t.Fatal(err) } out := &v1.Pod{} if err := out.Unmarshal(data); err != nil { t.Fatal(err) } if !api.Semantic.Equalities.DeepEqual(out, obj) { t.Logf("marshal\n%s", hex.Dump(data)) t.Fatalf("Unmarshal is unequal\n%s", diff.ObjectGoPrintDiff(out, obj)) } }
func TestProtobufRoundTrip(t *testing.T) { obj := &v1.Pod{} apitesting.FuzzerFor(t, v1.SchemeGroupVersion, rand.NewSource(benchmarkSeed)).Fuzz(obj) // InitContainers are turned into annotations by conversion. obj.Spec.InitContainers = nil obj.Status.InitContainerStatuses = nil // We don't round trip VolumeSource.Metadata (it's a deprecated OpenShift // carry) for protobuf, so we need to nil it out here to make the test pass. for i := range obj.Spec.Volumes { obj.Spec.Volumes[i].VolumeSource.Metadata = nil } data, err := obj.Marshal() if err != nil { t.Fatal(err) } out := &v1.Pod{} if err := out.Unmarshal(data); err != nil { t.Fatal(err) } if !api.Semantic.Equalities.DeepEqual(out, obj) { t.Logf("marshal\n%s", hex.Dump(data)) t.Fatalf("Unmarshal is unequal\n%s", diff.ObjectGoPrintDiff(out, obj)) } }
func TestAnonymousConfig(t *testing.T) { f := fuzz.New().NilChance(0.0).NumElements(1, 1) f.Funcs( func(r *runtime.Codec, f fuzz.Continue) {}, func(r *http.RoundTripper, f fuzz.Continue) {}, func(fn *func(http.RoundTripper) http.RoundTripper, f fuzz.Continue) {}, ) for i := 0; i < 20; i++ { original := &restclient.Config{} f.Fuzz(original) actual := AnonymousClientConfig(original) expected := *original // this is the list of known security related fields, add to this list if a new field // is added to restclient.Config, update AnonymousClientConfig to preserve the field otherwise. expected.Impersonate = "" expected.BearerToken = "" expected.Username = "" expected.Password = "" expected.TLSClientConfig.CertData = nil expected.TLSClientConfig.CertFile = "" expected.TLSClientConfig.KeyData = nil expected.TLSClientConfig.KeyFile = "" if !reflect.DeepEqual(actual, expected) { t.Fatalf("AnonymousClientConfig dropped unexpected fields, identify whether they are security related or not: %s", diff.ObjectGoPrintDiff(expected, actual)) } } }
func TestObjectWatchFraming(t *testing.T) { f := apitesting.FuzzerFor(nil, api.SchemeGroupVersion, rand.NewSource(benchmarkSeed)) secret := &api.Secret{} f.Fuzz(secret) secret.Data["binary"] = []byte{0x00, 0x10, 0x30, 0x55, 0xff, 0x00} secret.Data["utf8"] = []byte("a string with \u0345 characters") secret.Data["long"] = bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x00}, 1000) converted, _ := api.Scheme.ConvertToVersion(secret, "v1") v1secret := converted.(*v1.Secret) for _, streamingMediaType := range api.Codecs.SupportedStreamingMediaTypes() { s, framer, mediaType, _ := api.Codecs.StreamingSerializerForMediaType(streamingMediaType, nil) // TODO: remove this when the runtime.SerializerInfo PR lands if mediaType == "application/vnd.kubernetes.protobuf;stream=watch" { mediaType = "application/vnd.kubernetes.protobuf" } embedded, ok := api.Codecs.SerializerForMediaType(mediaType, nil) if !ok { t.Logf("no embedded serializer for %s", mediaType) embedded = s } innerDecode := api.Codecs.DecoderToVersion(embedded, api.SchemeGroupVersion) //innerEncode := api.Codecs.EncoderForVersion(embedded, api.SchemeGroupVersion) // write a single object through the framer and back out obj := &bytes.Buffer{} if err := s.EncodeToStream(v1secret, obj); err != nil { t.Fatal(err) } out := &bytes.Buffer{} w := framer.NewFrameWriter(out) if n, err := w.Write(obj.Bytes()); err != nil || n != len(obj.Bytes()) { t.Fatal(err) } sr := streaming.NewDecoder(framer.NewFrameReader(ioutil.NopCloser(out)), s) resultSecret := &v1.Secret{} res, _, err := sr.Decode(nil, resultSecret) if err != nil { t.Fatalf("%v:\n%s", err, hex.Dump(obj.Bytes())) } resultSecret.Kind = "Secret" resultSecret.APIVersion = "v1" if !api.Semantic.DeepEqual(v1secret, res) { t.Fatalf("objects did not match: %s", diff.ObjectGoPrintDiff(v1secret, res)) } // write a watch event through and back out obj = &bytes.Buffer{} if err := embedded.EncodeToStream(v1secret, obj); err != nil { t.Fatal(err) } event := &versioned.Event{Type: string(watch.Added)} event.Object.Raw = obj.Bytes() obj = &bytes.Buffer{} if err := s.EncodeToStream(event, obj); err != nil { t.Fatal(err) } out = &bytes.Buffer{} w = framer.NewFrameWriter(out) if n, err := w.Write(obj.Bytes()); err != nil || n != len(obj.Bytes()) { t.Fatal(err) } sr = streaming.NewDecoder(framer.NewFrameReader(ioutil.NopCloser(out)), s) outEvent := &versioned.Event{} res, _, err = sr.Decode(nil, outEvent) if err != nil || outEvent.Type != string(watch.Added) { t.Fatalf("%v: %#v", err, outEvent) } if outEvent.Object.Object == nil && outEvent.Object.Raw != nil { outEvent.Object.Object, err = runtime.Decode(innerDecode, outEvent.Object.Raw) if err != nil { t.Fatalf("%v:\n%s", err, hex.Dump(outEvent.Object.Raw)) } } if !api.Semantic.DeepEqual(secret, outEvent.Object.Object) { t.Fatalf("%s: did not match after frame decoding: %s", streamingMediaType, diff.ObjectGoPrintDiff(secret, outEvent.Object.Object)) } } }
func TestAddServices(t *testing.T) { tests := []struct { name string input Objects firstOnly bool expectedServices Objects }{ { name: "single port", input: Objects{ fakeDeploymentConfig("singleport", containerDesc{"test", []portDesc{{100, "tcp"}}}), }, expectedServices: Objects{ expectedService("singleport", portDesc{100, "tcp"}), }, }, { name: "multiple containers", input: Objects{ fakeDeploymentConfig("multicontainer", containerDesc{"test1", []portDesc{{100, "tcp"}}}, containerDesc{"test2", []portDesc{{200, "udp"}}}, ), }, expectedServices: Objects{ expectedService("multicontainer", portDesc{100, "tcp"}, portDesc{200, "udp"}), }, }, { name: "duplicate ports", input: Objects{ fakeDeploymentConfig("dupports", containerDesc{"test1", []portDesc{{80, "tcp"}, {25, "tcp"}}}, containerDesc{"test2", []portDesc{{80, "tcp"}}}, ), }, expectedServices: Objects{ expectedService("dupports", portDesc{25, "tcp"}, portDesc{80, "tcp"}), }, }, { name: "multiple deployment configs", input: Objects{ fakeDeploymentConfig("multidc1", containerDesc{"test1", []portDesc{{100, "tcp"}, {200, "udp"}}}, containerDesc{"test2", []portDesc{{300, "tcp"}}}, ), fakeDeploymentConfig("multidc2", containerDesc{"dc2_test1", []portDesc{{300, "tcp"}}}, containerDesc{"dc2_test2", []portDesc{{200, "udp"}, {300, "tcp"}}}, ), }, expectedServices: Objects{ expectedService("multidc1", portDesc{100, "tcp"}, portDesc{200, "udp"}, portDesc{300, "tcp"}), expectedService("multidc2", portDesc{200, "udp"}, portDesc{300, "tcp"}), }, }, { name: "first only", input: Objects{ fakeDeploymentConfig("firstonly", containerDesc{"test1", []portDesc{{80, "tcp"}, {25, "tcp"}, {100, "udp"}}}, ), }, expectedServices: Objects{ expectedService("firstonly", portDesc{25, "tcp"}), }, firstOnly: true, }, } for _, test := range tests { output := AddServices(test.input, test.firstOnly) services := getServices(output) if !reflect.DeepEqual(services, test.expectedServices) { t.Errorf("%s: did not get expected output: %s", test.name, diff.ObjectGoPrintDiff(test.expectedServices, services)) } } }
func TestKubeletDefaults(t *testing.T) { defaults := kubeletoptions.NewKubeletServer() // This is a snapshot of the default config // If the default changes (new fields are added, or default values change), we want to know // Once we've reacted to the changes appropriately in BuildKubernetesNodeConfig(), update this expected default to match the new upstream defaults expectedDefaults := &kubeletoptions.KubeletServer{ AuthPath: util.NewStringFlag("/var/lib/kubelet/kubernetes_auth"), KubeConfig: util.NewStringFlag("/var/lib/kubelet/kubeconfig"), SystemReserved: utilconfig.ConfigurationMap{}, KubeReserved: utilconfig.ConfigurationMap{}, KubeletConfiguration: componentconfig.KubeletConfiguration{ Address: "0.0.0.0", // overridden AllowPrivileged: false, // overridden CAdvisorPort: 4194, // disabled VolumeStatsAggPeriod: unversioned.Duration{Duration: time.Minute}, CertDirectory: "/var/run/kubernetes", CgroupRoot: "", ClusterDNS: "", // overridden ClusterDomain: "", // overridden ConfigureCBR0: false, ContainerRuntime: "docker", Containerized: false, // overridden based on OPENSHIFT_CONTAINERIZED CPUCFSQuota: true, // forced to true DockerExecHandlerName: "native", EventBurst: 10, EventRecordQPS: 5.0, EnableCustomMetrics: false, EnableDebuggingHandlers: true, EnableServer: true, FileCheckFrequency: unversioned.Duration{Duration: 20 * time.Second}, // overridden HealthzBindAddress: "127.0.0.1", // disabled HealthzPort: 10248, // disabled HostNetworkSources: "*", // overridden HostPIDSources: "*", // overridden HostIPCSources: "*", // overridden HTTPCheckFrequency: unversioned.Duration{Duration: 20 * time.Second}, // disabled ImageMinimumGCAge: unversioned.Duration{Duration: 2 * time.Minute}, ImageGCHighThresholdPercent: 90, ImageGCLowThresholdPercent: 80, LowDiskSpaceThresholdMB: 256, MasterServiceNamespace: "default", MaxContainerCount: 240, MaxPerPodContainerCount: 2, MaxOpenFiles: 1000000, MaxPods: 110, // overridden MinimumGCAge: unversioned.Duration{Duration: 1 * time.Minute}, NetworkPluginDir: "/usr/libexec/kubernetes/kubelet-plugins/net/exec/", NetworkPluginName: "", // overridden NonMasqueradeCIDR: "10.0.0.0/8", VolumePluginDir: "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/", NodeStatusUpdateFrequency: unversioned.Duration{Duration: 10 * time.Second}, NodeLabels: map[string]string{}, OOMScoreAdj: -999, LockFilePath: "", PodInfraContainerImage: kubeletoptions.GetDefaultPodInfraContainerImage(), // overridden Port: 10250, // overridden ReadOnlyPort: 10255, // disabled RegisterNode: true, RegisterSchedulable: true, RegistryBurst: 10, RegistryPullQPS: 5.0, ResolverConfig: kubetypes.ResolvConfDefault, KubeletCgroups: "", RktAPIEndpoint: rkt.DefaultRktAPIServiceEndpoint, RktPath: "", RktStage1Image: "", RootDirectory: "/var/lib/kubelet", // overridden RuntimeCgroups: "", SerializeImagePulls: true, StreamingConnectionIdleTimeout: unversioned.Duration{Duration: 4 * time.Hour}, SyncFrequency: unversioned.Duration{Duration: 1 * time.Minute}, SystemCgroups: "", TLSCertFile: "", // overridden to prevent cert generation TLSPrivateKeyFile: "", // overridden to prevent cert generation ReconcileCIDR: true, KubeAPIQPS: 5.0, KubeAPIBurst: 10, ExperimentalFlannelOverlay: false, OutOfDiskTransitionFrequency: unversioned.Duration{Duration: 5 * time.Minute}, HairpinMode: "promiscuous-bridge", BabysitDaemons: false, }, } if !reflect.DeepEqual(defaults, expectedDefaults) { t.Logf("expected defaults, actual defaults: \n%s", diff.ObjectGoPrintDiff(expectedDefaults, defaults)) t.Errorf("Got different defaults than expected, adjust in BuildKubernetesNodeConfig and update expectedDefaults") } }
func (tc *patchTestCase) Run(t *testing.T) { t.Logf("Starting test %s", tc.name) namespace := tc.startingPod.Namespace name := tc.startingPod.Name codec := testapi.Default.Codec() admit := tc.admit if admit == nil { admit = func(updatedObject runtime.Object, currentObject runtime.Object) error { return nil } } testPatcher := &testPatcher{} testPatcher.t = t testPatcher.startingPod = tc.startingPod testPatcher.updatePod = tc.updatePod ctx := api.NewDefaultContext() ctx = api.WithNamespace(ctx, namespace) namer := &testNamer{namespace, name} copier := runtime.ObjectCopier(api.Scheme) resource := unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"} versionedObj := &v1.Pod{} for _, patchType := range []api.PatchType{api.JSONPatchType, api.MergePatchType, api.StrategicMergePatchType} { // TODO SUPPORT THIS! if patchType == api.JSONPatchType { continue } t.Logf("Working with patchType %v", patchType) originalObjJS, err := runtime.Encode(codec, tc.startingPod) if err != nil { t.Errorf("%s: unexpected error: %v", tc.name, err) return } changedJS, err := runtime.Encode(codec, tc.changedPod) if err != nil { t.Errorf("%s: unexpected error: %v", tc.name, err) return } patch := []byte{} switch patchType { case api.JSONPatchType: continue case api.StrategicMergePatchType: patch, err = strategicpatch.CreateStrategicMergePatch(originalObjJS, changedJS, versionedObj) if err != nil { t.Errorf("%s: unexpected error: %v", tc.name, err) return } case api.MergePatchType: patch, err = jsonpatch.CreateMergePatch(originalObjJS, changedJS) if err != nil { t.Errorf("%s: unexpected error: %v", tc.name, err) return } } resultObj, err := patchResource(ctx, admit, 1*time.Second, versionedObj, testPatcher, name, patchType, patch, namer, copier, resource, codec) if len(tc.expectedError) != 0 { if err == nil || err.Error() != tc.expectedError { t.Errorf("%s: expected error %v, but got %v", tc.name, tc.expectedError, err) return } } else { if err != nil { t.Errorf("%s: unexpected error: %v", tc.name, err) return } } if tc.expectedPod == nil { if resultObj != nil { t.Errorf("%s: unexpected result: %v", tc.name, resultObj) } return } resultPod := resultObj.(*api.Pod) // roundtrip to get defaulting expectedJS, err := runtime.Encode(codec, tc.expectedPod) if err != nil { t.Errorf("%s: unexpected error: %v", tc.name, err) return } expectedObj, err := runtime.Decode(codec, expectedJS) if err != nil { t.Errorf("%s: unexpected error: %v", tc.name, err) return } reallyExpectedPod := expectedObj.(*api.Pod) if !reflect.DeepEqual(*reallyExpectedPod, *resultPod) { t.Errorf("%s mismatch: %v\n", tc.name, diff.ObjectGoPrintDiff(reallyExpectedPod, resultPod)) return } } }
func TestObjectWatchFraming(t *testing.T) { f := apitesting.FuzzerFor(nil, api.SchemeGroupVersion, rand.NewSource(benchmarkSeed)) secret := &api.Secret{} f.Fuzz(secret) secret.Data["binary"] = []byte{0x00, 0x10, 0x30, 0x55, 0xff, 0x00} secret.Data["utf8"] = []byte("a string with \u0345 characters") secret.Data["long"] = bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x00}, 1000) converted, _ := api.Scheme.ConvertToVersion(secret, v1.SchemeGroupVersion) v1secret := converted.(*v1.Secret) for _, info := range api.Codecs.SupportedMediaTypes() { if info.StreamSerializer == nil { continue } s := info.StreamSerializer framer := s.Framer embedded := info.Serializer if embedded == nil { t.Errorf("no embedded serializer for %s", info.MediaType) continue } innerDecode := api.Codecs.DecoderToVersion(embedded, api.SchemeGroupVersion) // write a single object through the framer and back out obj := &bytes.Buffer{} if err := s.Encode(v1secret, obj); err != nil { t.Fatal(err) } out := &bytes.Buffer{} w := framer.NewFrameWriter(out) if n, err := w.Write(obj.Bytes()); err != nil || n != len(obj.Bytes()) { t.Fatal(err) } sr := streaming.NewDecoder(framer.NewFrameReader(ioutil.NopCloser(out)), s) resultSecret := &v1.Secret{} res, _, err := sr.Decode(nil, resultSecret) if err != nil { t.Fatalf("%v:\n%s", err, hex.Dump(obj.Bytes())) } resultSecret.Kind = "Secret" resultSecret.APIVersion = "v1" if !api.Semantic.DeepEqual(v1secret, res) { t.Fatalf("objects did not match: %s", diff.ObjectGoPrintDiff(v1secret, res)) } // write a watch event through and back out obj = &bytes.Buffer{} if err := embedded.Encode(v1secret, obj); err != nil { t.Fatal(err) } event := &versioned.Event{Type: string(watch.Added)} event.Object.Raw = obj.Bytes() obj = &bytes.Buffer{} if err := s.Encode(event, obj); err != nil { t.Fatal(err) } out = &bytes.Buffer{} w = framer.NewFrameWriter(out) if n, err := w.Write(obj.Bytes()); err != nil || n != len(obj.Bytes()) { t.Fatal(err) } sr = streaming.NewDecoder(framer.NewFrameReader(ioutil.NopCloser(out)), s) outEvent := &versioned.Event{} res, _, err = sr.Decode(nil, outEvent) if err != nil || outEvent.Type != string(watch.Added) { t.Fatalf("%v: %#v", err, outEvent) } if outEvent.Object.Object == nil && outEvent.Object.Raw != nil { outEvent.Object.Object, err = runtime.Decode(innerDecode, outEvent.Object.Raw) if err != nil { t.Fatalf("%v:\n%s", err, hex.Dump(outEvent.Object.Raw)) } } if !api.Semantic.DeepEqual(secret, outEvent.Object.Object) { t.Fatalf("%s: did not match after frame decoding: %s", info.MediaType, diff.ObjectGoPrintDiff(secret, outEvent.Object.Object)) } } }
func TestAnonymousConfig(t *testing.T) { f := fuzz.New().NilChance(0.0).NumElements(1, 1) f.Funcs( func(r *runtime.Codec, f fuzz.Continue) { codec := &fakeCodec{} f.Fuzz(codec) *r = codec }, func(r *http.RoundTripper, f fuzz.Continue) { roundTripper := &fakeRoundTripper{} f.Fuzz(roundTripper) *r = roundTripper }, func(fn *func(http.RoundTripper) http.RoundTripper, f fuzz.Continue) { *fn = fakeWrapperFunc }, func(r *runtime.NegotiatedSerializer, f fuzz.Continue) { serializer := &fakeNegotiatedSerializer{} f.Fuzz(serializer) *r = serializer }, func(r *flowcontrol.RateLimiter, f fuzz.Continue) { limiter := &fakeLimiter{} f.Fuzz(limiter) *r = limiter }, // Authentication does not require fuzzer func(r *AuthProviderConfigPersister, f fuzz.Continue) {}, func(r *clientcmdapi.AuthProviderConfig, f fuzz.Continue) { r.Config = map[string]string{} }, ) for i := 0; i < 20; i++ { original := &Config{} f.Fuzz(original) actual := AnonymousClientConfig(original) expected := *original // this is the list of known security related fields, add to this list if a new field // is added to Config, update AnonymousClientConfig to preserve the field otherwise. expected.Impersonate = ImpersonationConfig{} expected.BearerToken = "" expected.Username = "" expected.Password = "" expected.AuthProvider = nil expected.AuthConfigPersister = nil expected.TLSClientConfig.CertData = nil expected.TLSClientConfig.CertFile = "" expected.TLSClientConfig.KeyData = nil expected.TLSClientConfig.KeyFile = "" // The DeepEqual cannot handle the func comparison, so we just verify if the // function return the expected object. if actual.WrapTransport == nil || !reflect.DeepEqual(expected.WrapTransport(nil), &fakeRoundTripper{}) { t.Fatalf("AnonymousClientConfig dropped the WrapTransport field") } else { actual.WrapTransport = nil expected.WrapTransport = nil } if !reflect.DeepEqual(*actual, expected) { t.Fatalf("AnonymousClientConfig dropped unexpected fields, identify whether they are security related or not: %s", diff.ObjectGoPrintDiff(expected, actual)) } } }
func TestCMServerDefaults(t *testing.T) { defaults := cmapp.NewCMServer() // This is a snapshot of the default config // If the default changes (new fields are added, or default values change), we want to know // Once we've reacted to the changes appropriately in BuildKubernetesMasterConfig(), update this expected default to match the new upstream defaults expectedDefaults := &cmapp.CMServer{ KubeControllerManagerConfiguration: componentconfig.KubeControllerManagerConfiguration{ Port: 10252, // disabled Address: "0.0.0.0", ConcurrentEndpointSyncs: 5, ConcurrentRCSyncs: 5, ConcurrentRSSyncs: 5, ConcurrentDaemonSetSyncs: 2, ConcurrentJobSyncs: 5, ConcurrentResourceQuotaSyncs: 5, ConcurrentDeploymentSyncs: 5, ConcurrentNamespaceSyncs: 2, ConcurrentSATokenSyncs: 5, LookupCacheSizeForRC: 4096, LookupCacheSizeForRS: 4096, LookupCacheSizeForDaemonSet: 1024, ServiceSyncPeriod: unversioned.Duration{Duration: 5 * time.Minute}, NodeSyncPeriod: unversioned.Duration{Duration: 10 * time.Second}, ResourceQuotaSyncPeriod: unversioned.Duration{Duration: 5 * time.Minute}, NamespaceSyncPeriod: unversioned.Duration{Duration: 5 * time.Minute}, PVClaimBinderSyncPeriod: unversioned.Duration{Duration: 10 * time.Minute}, HorizontalPodAutoscalerSyncPeriod: unversioned.Duration{Duration: 30 * time.Second}, DeploymentControllerSyncPeriod: unversioned.Duration{Duration: 30 * time.Second}, MinResyncPeriod: unversioned.Duration{Duration: 12 * time.Hour}, RegisterRetryCount: 10, PodEvictionTimeout: unversioned.Duration{Duration: 5 * time.Minute}, NodeMonitorGracePeriod: unversioned.Duration{Duration: 40 * time.Second}, NodeStartupGracePeriod: unversioned.Duration{Duration: 60 * time.Second}, NodeMonitorPeriod: unversioned.Duration{Duration: 5 * time.Second}, ClusterName: "kubernetes", TerminatedPodGCThreshold: 12500, VolumeConfiguration: componentconfig.VolumeConfiguration{ EnableHostPathProvisioning: false, PersistentVolumeRecyclerConfiguration: componentconfig.PersistentVolumeRecyclerConfiguration{ MaximumRetry: 3, MinimumTimeoutNFS: 300, IncrementTimeoutNFS: 30, MinimumTimeoutHostPath: 60, IncrementTimeoutHostPath: 30, }, }, KubeAPIQPS: 20.0, KubeAPIBurst: 30, LeaderElection: componentconfig.LeaderElectionConfiguration{ LeaderElect: false, LeaseDuration: unversioned.Duration{Duration: 15 * time.Second}, RenewDeadline: unversioned.Duration{Duration: 10 * time.Second}, RetryPeriod: unversioned.Duration{Duration: 2 * time.Second}, }, }, } if !reflect.DeepEqual(defaults, expectedDefaults) { t.Logf("expected defaults, actual defaults: \n%s", diff.ObjectGoPrintDiff(expectedDefaults, defaults)) t.Errorf("Got different defaults than expected, adjust in BuildKubernetesMasterConfig and update expectedDefaults") } }
func TestWebhook(t *testing.T) { serv := new(recorderService) s, err := NewTestServer(serv, serverCert, serverKey, caCert) if err != nil { t.Fatal(err) } defer s.Close() wh, err := newAuthorizer(s.URL, clientCert, clientKey, caCert) if err != nil { t.Fatal(err) } expTypeMeta := unversioned.TypeMeta{ APIVersion: "authorization.k8s.io/v1beta1", Kind: "SubjectAccessReview", } tests := []struct { attr authorizer.Attributes want v1beta1.SubjectAccessReview }{ { attr: authorizer.AttributesRecord{User: &user.DefaultInfo{}}, want: v1beta1.SubjectAccessReview{ TypeMeta: expTypeMeta, Spec: v1beta1.SubjectAccessReviewSpec{ NonResourceAttributes: &v1beta1.NonResourceAttributes{}, }, }, }, { attr: authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "jane"}}, want: v1beta1.SubjectAccessReview{ TypeMeta: expTypeMeta, Spec: v1beta1.SubjectAccessReviewSpec{ User: "******", NonResourceAttributes: &v1beta1.NonResourceAttributes{}, }, }, }, { attr: authorizer.AttributesRecord{ User: &user.DefaultInfo{ Name: "jane", UID: "1", Groups: []string{"group1", "group2"}, }, Verb: "GET", Namespace: "kittensandponies", APIGroup: "group3", Resource: "pods", ResourceRequest: true, Path: "/foo", }, want: v1beta1.SubjectAccessReview{ TypeMeta: expTypeMeta, Spec: v1beta1.SubjectAccessReviewSpec{ User: "******", Groups: []string{"group1", "group2"}, ResourceAttributes: &v1beta1.ResourceAttributes{ Verb: "GET", Namespace: "kittensandponies", Group: "group3", Resource: "pods", }, }, }, }, } for i, tt := range tests { if err := wh.Authorize(tt.attr); err != nil { t.Errorf("case %d: authorization failed: %v", i, err) continue } gotAttr, err := serv.Last() if err != nil { t.Errorf("case %d: failed to deserialize webhook request: %v", i, err) continue } if !reflect.DeepEqual(gotAttr, tt.want) { t.Errorf("case %d: got != want:\n%s", i, diff.ObjectGoPrintDiff(gotAttr, tt.want)) } } }
func TestDecodeObjects(t *testing.T) { obj1 := &v1.Pod{ ObjectMeta: v1.ObjectMeta{ Name: "cool", }, Spec: v1.PodSpec{ Containers: []v1.Container{ { Name: "test", }, }, }, } obj1wire, err := obj1.Marshal() if err != nil { t.Fatal(err) } wire1, err := (&runtime.Unknown{ TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: "v1"}, Raw: obj1wire, }).Marshal() if err != nil { t.Fatal(err) } unk2 := &runtime.Unknown{ TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: "v1"}, } wire2 := make([]byte, len(wire1)*2) n, err := unk2.NestedMarshalTo(wire2, obj1, uint64(obj1.Size())) if err != nil { t.Fatal(err) } if n != len(wire1) || !bytes.Equal(wire1, wire2[:n]) { t.Fatalf("unexpected wire:\n%s", hex.Dump(wire2[:n])) } wire1 = append([]byte{0x6b, 0x38, 0x73, 0x00}, wire1...) testCases := []struct { obj runtime.Object data []byte errFn func(error) bool }{ { obj: obj1, data: wire1, }, } for i, test := range testCases { s := protobuf.NewSerializer(api.Scheme, runtime.ObjectTyperToTyper(api.Scheme), "application/protobuf") obj, err := runtime.Decode(s, test.data) switch { case err == nil && test.errFn != nil: t.Errorf("%d: failed: %v", i, err) continue case err != nil && test.errFn == nil: t.Errorf("%d: failed: %v", i, err) continue case err != nil: if !test.errFn(err) { t.Errorf("%d: failed: %v", i, err) } if obj != nil { t.Errorf("%d: should not have returned an object", i) } continue } if !api.Semantic.DeepEqual(obj, test.obj) { t.Errorf("%d: unexpected object:\n%s", i, diff.ObjectGoPrintDiff(test.obj, obj)) continue } } }
func TestRepositoryBlobStat(t *testing.T) { quotaEnforcing = "aEnforcingConfig{} ctx := context.Background() // this driver holds all the testing blobs in memory during the whole test run driver := inmemory.New() // generate two images and store their blobs in the driver testImages, err := populateTestStorage(t, driver, true, 1, map[string]int{"nm/is:latest": 1, "nm/repo:missing-layer-links": 1}, nil) if err != nil { t.Fatal(err) } // generate an image and store its blobs in the driver; the resulting image will lack managed by openshift // annotation testImages, err = populateTestStorage(t, driver, false, 1, map[string]int{"nm/unmanaged:missing-layer-links": 1}, testImages) if err != nil { t.Fatal(err) } // remove layer repository links from two of the above images; keep the uploaded blobs in the global // blostore though for _, name := range []string{"nm/repo:missing-layer-links", "nm/unmanaged:missing-layer-links"} { repoName := strings.Split(name, ":")[0] for _, layer := range testImages[name][0].DockerImageLayers { dgst := digest.Digest(layer.Name) alg, hex := dgst.Algorithm(), dgst.Hex() err := driver.Delete(ctx, fmt.Sprintf("/docker/registry/v2/repositories/%s/_layers/%s/%s", repoName, alg, hex)) if err != nil { t.Fatalf("failed to delete layer link %q from repository %q: %v", layer.Name, repoName, err) } } } // generate random images without storing its blobs in the driver etcdOnlyImages := map[string]*imageapi.Image{} for _, d := range []struct { name string managed bool }{{"nm/is", true}, {"registry.org:5000/user/app", false}} { img, err := registrytest.NewImageForManifest(d.name, registrytest.SampleImageManifestSchema1, d.managed) if err != nil { t.Fatal(err) } etcdOnlyImages[d.name] = img } for _, tc := range []struct { name string stat string images []imageapi.Image imageStreams []imageapi.ImageStream pullthrough bool skipAuth bool deferredErrors deferredErrors expectedDescriptor distribution.Descriptor expectedError error expectedActions []clientAction }{ { name: "local stat", stat: "nm/is@" + testImages["nm/is:latest"][0].DockerImageLayers[0].Name, imageStreams: []imageapi.ImageStream{{ObjectMeta: kapi.ObjectMeta{Namespace: "nm", Name: "is"}}}, expectedDescriptor: testNewDescriptorForLayer(testImages["nm/is:latest"][0].DockerImageLayers[0]), }, { name: "blob only tagged in image stream", stat: "nm/repo@" + testImages["nm/repo:missing-layer-links"][0].DockerImageLayers[1].Name, images: []imageapi.Image{*testImages["nm/repo:missing-layer-links"][0]}, imageStreams: []imageapi.ImageStream{ { ObjectMeta: kapi.ObjectMeta{ Namespace: "nm", Name: "repo", }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { Image: testImages["nm/repo:missing-layer-links"][0].Name, }, }, }, }, }, }, }, expectedDescriptor: testNewDescriptorForLayer(testImages["nm/repo:missing-layer-links"][0].DockerImageLayers[1]), expectedActions: []clientAction{{"get", "imagestreams"}, {"get", "images"}}, }, { name: "blob referenced only by not managed image with pullthrough on", stat: "nm/unmanaged@" + testImages["nm/unmanaged:missing-layer-links"][0].DockerImageLayers[1].Name, images: []imageapi.Image{*testImages["nm/unmanaged:missing-layer-links"][0]}, imageStreams: []imageapi.ImageStream{ { ObjectMeta: kapi.ObjectMeta{ Namespace: "nm", Name: "unmanaged", }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { Image: testImages["nm/unmanaged:missing-layer-links"][0].Name, }, }, }, }, }, }, }, pullthrough: true, expectedDescriptor: testNewDescriptorForLayer(testImages["nm/unmanaged:missing-layer-links"][0].DockerImageLayers[1]), expectedActions: []clientAction{{"get", "imagestreams"}, {"get", "images"}}, }, { // TODO: this should err out because of missing image stream. // Unfortunately, it's not the case. Until we start storing layer links in etcd, we depend on // local layer links. name: "layer link present while image stream not found", stat: "nm/is@" + testImages["nm/is:latest"][0].DockerImageLayers[0].Name, images: []imageapi.Image{*testImages["nm/is:latest"][0]}, expectedDescriptor: testNewDescriptorForLayer(testImages["nm/is:latest"][0].DockerImageLayers[0]), }, { name: "blob only tagged by not managed image with pullthrough off", stat: "nm/repo@" + testImages["nm/unmanaged:missing-layer-links"][0].DockerImageLayers[1].Name, images: []imageapi.Image{*testImages["nm/unmanaged:missing-layer-links"][0]}, imageStreams: []imageapi.ImageStream{ { ObjectMeta: kapi.ObjectMeta{ Namespace: "nm", Name: "repo", }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { Image: testImages["nm/unmanaged:missing-layer-links"][0].DockerImageLayers[1].Name, }, }, }, }, }, }, }, expectedError: distribution.ErrBlobUnknown, expectedActions: []clientAction{{"get", "imagestreams"}, {"get", "images"}}, }, { name: "blob not stored locally but referred in image stream", stat: "nm/is@" + etcdOnlyImages["nm/is"].DockerImageLayers[1].Name, images: []imageapi.Image{*etcdOnlyImages["nm/is"]}, imageStreams: []imageapi.ImageStream{ { ObjectMeta: kapi.ObjectMeta{ Namespace: "nm", Name: "is", }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { Image: etcdOnlyImages["nm/is"].Name, }, }, }, }, }, }, }, expectedError: distribution.ErrBlobUnknown, }, { name: "blob does not exist", stat: "nm/repo@" + etcdOnlyImages["nm/is"].DockerImageLayers[0].Name, images: []imageapi.Image{*testImages["nm/is:latest"][0]}, imageStreams: []imageapi.ImageStream{ { ObjectMeta: kapi.ObjectMeta{ Namespace: "nm", Name: "repo", }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { Image: testImages["nm/is:latest"][0].Name, }, }, }, }, }, }, }, expectedError: distribution.ErrBlobUnknown, }, { name: "auth not performed", stat: "nm/is@" + testImages["nm/is:latest"][0].DockerImageLayers[0].Name, imageStreams: []imageapi.ImageStream{{ObjectMeta: kapi.ObjectMeta{Namespace: "nm", Name: "is"}}}, skipAuth: true, expectedError: fmt.Errorf("openshift.auth.completed missing from context"), }, { name: "deferred error", stat: "nm/is@" + testImages["nm/is:latest"][0].DockerImageLayers[0].Name, imageStreams: []imageapi.ImageStream{{ObjectMeta: kapi.ObjectMeta{Namespace: "nm", Name: "is"}}}, deferredErrors: deferredErrors{"nm/is": ErrOpenShiftAccessDenied}, expectedError: ErrOpenShiftAccessDenied, }, } { ref, err := reference.Parse(tc.stat) if err != nil { t.Errorf("[%s] failed to parse blob reference %q: %v", tc.name, tc.stat, err) continue } canonical, ok := ref.(reference.Canonical) if !ok { t.Errorf("[%s] not a canonical reference %q", tc.name, ref.String()) continue } cachedLayers, err = newDigestToRepositoryCache(defaultDigestToRepositoryCacheSize) if err != nil { t.Fatal(err) } ctx := context.Background() if !tc.skipAuth { ctx = WithAuthPerformed(ctx) } if tc.deferredErrors != nil { ctx = WithDeferredErrors(ctx, tc.deferredErrors) } client := &testclient.Fake{} client.AddReactor("get", "imagestreams", imagetest.GetFakeImageStreamGetHandler(t, tc.imageStreams...)) client.AddReactor("get", "images", registrytest.GetFakeImageGetHandler(t, tc.images...)) reg, err := newTestRegistry(ctx, client, driver, defaultBlobRepositoryCacheTTL, tc.pullthrough, true) if err != nil { t.Errorf("[%s] unexpected error: %v", tc.name, err) continue } repo, err := reg.Repository(ctx, canonical) if err != nil { t.Errorf("[%s] unexpected error: %v", tc.name, err) continue } desc, err := repo.Blobs(ctx).Stat(ctx, canonical.Digest()) if err != nil && tc.expectedError == nil { t.Errorf("[%s] got unexpected stat error: %v", tc.name, err) continue } if err == nil && tc.expectedError != nil { t.Errorf("[%s] got unexpected non-error", tc.name) continue } if !reflect.DeepEqual(err, tc.expectedError) { t.Errorf("[%s] got unexpected error: %s", tc.name, diff.ObjectGoPrintDiff(err, tc.expectedError)) continue } if tc.expectedError == nil && !reflect.DeepEqual(desc, tc.expectedDescriptor) { t.Errorf("[%s] got unexpected descriptor: %s", tc.name, diff.ObjectGoPrintDiff(desc, tc.expectedDescriptor)) } compareActions(t, tc.name, client.Actions(), tc.expectedActions) } }
func TestControllerStart(t *testing.T) { two := int64(2) testCases := []struct { stream *api.ImageStream run bool }{ { stream: &api.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Annotations: map[string]string{api.DockerImageRepositoryCheckAnnotation: unversioned.Now().UTC().Format(time.RFC3339)}, Name: "test", Namespace: "other", }, }, }, { stream: &api.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Annotations: map[string]string{api.DockerImageRepositoryCheckAnnotation: unversioned.Now().UTC().Format(time.RFC3339)}, Name: "test", Namespace: "other", }, Spec: api.ImageStreamSpec{ DockerImageRepository: "test/other", }, }, }, { stream: &api.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Annotations: map[string]string{api.DockerImageRepositoryCheckAnnotation: "a random error"}, Name: "test", Namespace: "other", }, Spec: api.ImageStreamSpec{ DockerImageRepository: "test/other", }, }, }, // references are ignored { stream: &api.ImageStream{ ObjectMeta: kapi.ObjectMeta{Name: "test", Namespace: "other"}, Spec: api.ImageStreamSpec{ Tags: map[string]api.TagReference{ "latest": { From: &kapi.ObjectReference{Kind: "DockerImage", Name: "test/other:latest"}, Reference: true, }, }, }, }, }, { stream: &api.ImageStream{ ObjectMeta: kapi.ObjectMeta{Name: "test", Namespace: "other"}, Spec: api.ImageStreamSpec{ Tags: map[string]api.TagReference{ "latest": { From: &kapi.ObjectReference{Kind: "AnotherImage", Name: "test/other:latest"}, Reference: true, }, }, }, }, }, // spec tag will be imported { run: true, stream: &api.ImageStream{ ObjectMeta: kapi.ObjectMeta{Name: "test", Namespace: "other"}, Spec: api.ImageStreamSpec{ Tags: map[string]api.TagReference{ "latest": { From: &kapi.ObjectReference{Kind: "DockerImage", Name: "test/other:latest"}, }, }, }, }, }, // spec tag with generation with no pending status will be imported { run: true, stream: &api.ImageStream{ ObjectMeta: kapi.ObjectMeta{Name: "test", Namespace: "other"}, Spec: api.ImageStreamSpec{ Tags: map[string]api.TagReference{ "latest": { From: &kapi.ObjectReference{Kind: "DockerImage", Name: "test/other:latest"}, Generation: &two, }, }, }, }, }, // spec tag with generation with older status generation will be imported { run: true, stream: &api.ImageStream{ ObjectMeta: kapi.ObjectMeta{Name: "test", Namespace: "other"}, Spec: api.ImageStreamSpec{ Tags: map[string]api.TagReference{ "latest": { From: &kapi.ObjectReference{Kind: "DockerImage", Name: "test/other:latest"}, Generation: &two, }, }, }, Status: api.ImageStreamStatus{ Tags: map[string]api.TagEventList{"latest": {Items: []api.TagEvent{{Generation: 1}}}}, }, }, }, // spec tag with generation with status condition error and equal generation will not be imported { stream: &api.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Annotations: map[string]string{api.DockerImageRepositoryCheckAnnotation: unversioned.Now().UTC().Format(time.RFC3339)}, Name: "test", Namespace: "other", }, Spec: api.ImageStreamSpec{ Tags: map[string]api.TagReference{ "latest": { From: &kapi.ObjectReference{Kind: "DockerImage", Name: "test/other:latest"}, Generation: &two, }, }, }, Status: api.ImageStreamStatus{ Tags: map[string]api.TagEventList{"latest": {Conditions: []api.TagEventCondition{ { Type: api.ImportSuccess, Status: kapi.ConditionFalse, Generation: 2, }, }}}, }, }, }, // spec tag with generation with status condition error and older generation will be imported { run: true, stream: &api.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Annotations: map[string]string{api.DockerImageRepositoryCheckAnnotation: unversioned.Now().UTC().Format(time.RFC3339)}, Name: "test", Namespace: "other", }, Spec: api.ImageStreamSpec{ Tags: map[string]api.TagReference{ "latest": { From: &kapi.ObjectReference{Kind: "DockerImage", Name: "test/other:latest"}, Generation: &two, }, }, }, Status: api.ImageStreamStatus{ Tags: map[string]api.TagEventList{"latest": {Conditions: []api.TagEventCondition{ { Type: api.ImportSuccess, Status: kapi.ConditionFalse, Generation: 1, }, }}}, }, }, }, // spec tag with generation with older status generation will be imported { run: true, stream: &api.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Annotations: map[string]string{api.DockerImageRepositoryCheckAnnotation: unversioned.Now().UTC().Format(time.RFC3339)}, Name: "test", Namespace: "other", }, Spec: api.ImageStreamSpec{ Tags: map[string]api.TagReference{ "latest": { From: &kapi.ObjectReference{Kind: "DockerImage", Name: "test/other:latest"}, Generation: &two, }, }, }, Status: api.ImageStreamStatus{ Tags: map[string]api.TagEventList{"latest": {Items: []api.TagEvent{{Generation: 1}}}}, }, }, }, } for i, test := range testCases { fake := &client.Fake{} c := ImportController{streams: fake} other, err := kapi.Scheme.DeepCopy(test.stream) if err != nil { t.Fatal(err) } if err := c.Next(test.stream, nil); err != nil { t.Errorf("%d: unexpected error: %v", i, err) } if test.run { if len(fake.Actions()) == 0 { t.Errorf("%d: expected remote calls: %#v", i, fake) } } else { if !kapi.Semantic.DeepEqual(test.stream, other) { t.Errorf("%d: did not expect change to stream: %s", i, diff.ObjectGoPrintDiff(test.stream, other)) } if len(fake.Actions()) != 0 { t.Errorf("%d: did not expect remote calls", i) } } } }