mirror of
https://github.com/kubernetes/client-go.git
synced 2025-07-20 18:10:24 +00:00
These issues were not found earlier because "make vet" ignored staging. Some of
these fixes are stylistic and/or don't matter in practice, but all of the
loopclosure issues seem to be real: those tests didn't run as intended.
Here's the full error report:
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go:304:11: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersion struct literal uses unkeyed fields (govet)
gv := schema.GroupVersion{crd.Spec.Group, v.Name}
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler_test.go:790:119: composites: k8s.io/apimachinery/pkg/runtime/serializer/json.SerializerOptions struct literal uses unkeyed fields (govet)
delegate := serializerjson.NewSerializerWithOptions(serializerjson.DefaultMetaFactory, unstructuredCreator{}, nil, serializerjson.SerializerOptions{tc.yaml, false, tc.strictDecoding})
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation.go:171:30: composites: k8s.io/apiserver/pkg/cel.Error struct literal uses unkeyed fields (govet)
compilationResult.Error = &apiservercel.Error{apiservercel.ErrorTypeInvalid, "compilation failed: " + issues.String()}
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation.go:175:30: composites: k8s.io/apiserver/pkg/cel.Error struct literal uses unkeyed fields (govet)
compilationResult.Error = &apiservercel.Error{apiservercel.ErrorTypeInvalid, "cel expression must evaluate to a bool"}
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation.go:182:30: composites: k8s.io/apiserver/pkg/cel.Error struct literal uses unkeyed fields (govet)
compilationResult.Error = &apiservercel.Error{apiservercel.ErrorTypeInternal, "unexpected compilation error: " + err.Error()}
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation.go:201:30: composites: k8s.io/apiserver/pkg/cel.Error struct literal uses unkeyed fields (govet)
compilationResult.Error = &apiservercel.Error{apiservercel.ErrorTypeInvalid, "program instantiation failed: " + err.Error()}
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation.go:206:30: composites: k8s.io/apiserver/pkg/cel.Error struct literal uses unkeyed fields (govet)
compilationResult.Error = &apiservercel.Error{apiservercel.ErrorTypeInternal, "cost estimation failed: " + err.Error()}
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:38:14: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"foo"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:44:15: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"foo"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:53:17: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"A"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:58:17: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"B"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:63:17: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"C"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:76:19: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"A"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:81:19: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"B"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:91:18: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"N"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:96:18: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"O"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:108:21: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"alpha"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:113:21: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"beta"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:123:16: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"bar"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:133:17: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"A"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:147:17: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"A"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:159:15: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"A"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:169:17: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"A"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:179:17: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"A"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:190:18: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"A"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go:202:18: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"A"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/prunenulls_test.go:38:14: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"foo"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/prunenulls_test.go:47:17: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"A"},
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/prunenulls_test.go:57:18: composites: k8s.io/apiextensions-apiserver/pkg/apiserver/schema.JSON struct literal uses unkeyed fields (govet)
Default: structuralschema.JSON{"C"},
^
staging/src/k8s.io/apiextensions-apiserver/test/integration/defaulting_test.go:289:38: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
^
staging/src/k8s.io/apiextensions-apiserver/test/integration/listtype_test.go:140:38: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
^
staging/src/k8s.io/apiextensions-apiserver/test/integration/objectmeta_test.go:453:38: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
^
staging/src/k8s.io/apiextensions-apiserver/test/integration/pruning_test.go:214:38: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
^
staging/src/k8s.io/apiextensions-apiserver/test/integration/pruning_test.go:266:38: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
^
staging/src/k8s.io/apiextensions-apiserver/test/integration/pruning_test.go:377:38: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
^
staging/src/k8s.io/apiextensions-apiserver/test/integration/pruning_test.go:418:38: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
^
staging/src/k8s.io/apiextensions-apiserver/test/integration/pruning_test.go:471:38: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
^
staging/src/k8s.io/apiextensions-apiserver/test/integration/pruning_test.go:556:38: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/celcoststability_test.go:1096:32: loopclosure: loop variable validRule captured by func literal (govet)
s := withRule(*tt.schema, validRule)
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/celcoststability_test.go:1107:19: loopclosure: loop variable expectedCost captured by func literal (govet)
if rtCost != expectedCost {
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/celcoststability_test.go:1108:83: loopclosure: loop variable expectedCost captured by func literal (govet)
t.Fatalf("runtime cost %d does not match expected runtime cost %d", rtCost, expectedCost)
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/validation_test.go:2009:30: loopclosure: loop variable tt captured by func literal (govet)
celValidator := validator(tt.schema, true, model.SchemaDeclType(tt.schema, true), PerCallLimit)
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/validation_test.go:2013:65: loopclosure: loop variable tt captured by func literal (govet)
errs, _ := celValidator.Validate(ctx, field.NewPath("root"), tt.schema, tt.obj, tt.oldObj, math.MaxInt)
^
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/validation_test.go:2015:22: loopclosure: loop variable tt captured by func literal (govet)
for _, e := range tt.errors {
^
staging/src/k8s.io/apimachinery/pkg/runtime/mapper_test.go:28:67: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
gvr := func(g, v, r string) schema.GroupVersionResource { return schema.GroupVersionResource{g, v, r} }
^
staging/src/k8s.io/apimachinery/pkg/runtime/mapper_test.go:30:63: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
gvk := func(g, v, k string) schema.GroupVersionKind { return schema.GroupVersionKind{g, v, k} }
^
staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning.go:150:35: composites: k8s.io/apimachinery/pkg/runtime.WithoutVersionDecoder struct literal uses unkeyed fields (govet)
if err := d.DecodeNestedObjects(runtime.WithoutVersionDecoder{c.decoder}); err != nil {
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace/matcher.go:119:18: composites: k8s.io/apimachinery/pkg/api/errors.StatusError struct literal uses unkeyed fields (govet)
return false, &apierrors.StatusError{status.Status()}
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go:300:17: composites: k8s.io/api/admissionregistration/v1.MutatingWebhook struct literal uses unkeyed fields (govet)
mutating[i] = registrationv1.MutatingWebhook{h.Name, h.ClientConfig, h.Rules, h.FailurePolicy, h.MatchPolicy, h.NamespaceSelector, h.ObjectSelector, h.SideEffects, h.TimeoutSeconds, h.AdmissionReviewVersions, nil}
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:70:25: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
mapper.RegisterKindFor(schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "", schema.GroupVersionKind{"extensions", "v1beta1", "Deployment"})
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:71:25: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1", "Deployment"})
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:72:25: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1beta1", "Deployment"})
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:73:25: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1alpha1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1alpha1", "Deployment"})
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:75:25: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
mapper.RegisterKindFor(schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "scale", schema.GroupVersionKind{"extensions", "v1beta1", "Scale"})
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:76:25: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", schema.GroupVersionKind{"autoscaling", "v1", "Scale"})
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:77:25: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "deployments"}, "scale", schema.GroupVersionKind{"apps", "v1beta1", "Scale"})
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:78:25: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1alpha1", "deployments"}, "scale", schema.GroupVersionKind{"apps", "v1alpha1", "Scale"})
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:81:25: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
mapper.RegisterKindFor(schema.GroupVersionResource{"example.com", "v1", "widgets"}, "", schema.GroupVersionKind{"", "", ""})
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:82:25: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
mapper.RegisterKindFor(schema.GroupVersionResource{"example.com", "v2", "widgets"}, "", schema.GroupVersionKind{"", "", ""})
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go💯59: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:114:61: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:116:22: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
expectMatchKind: &schema.GroupVersionKind{"apps", "v1", "Deployment"},
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:139:61: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:141:22: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
expectMatchKind: &schema.GroupVersionKind{"apps", "v1", "Deployment"},
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:159:59: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:179:59: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:199:61: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:201:22: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
expectMatchKind: &schema.GroupVersionKind{"extensions", "v1beta1", "Deployment"},
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:220:61: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:222:22: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
expectMatchKind: &schema.GroupVersionKind{"apps", "v1beta1", "Deployment"},
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:246:61: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:248:22: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
expectMatchKind: &schema.GroupVersionKind{"autoscaling", "v1", "Scale"},
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:266:59: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:286:59: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:306:61: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:308:22: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
expectMatchKind: &schema.GroupVersionKind{"extensions", "v1beta1", "Scale"},
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:327:61: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:329:22: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
expectMatchKind: &schema.GroupVersionKind{"apps", "v1beta1", "Scale"},
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:343:61: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:345:22: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
expectMatchKind: &schema.GroupVersionKind{"autoscaling", "v1", "Scale"},
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:359:59: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:375:61: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:377:22: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
expectMatchKind: &schema.GroupVersionKind{"autoscaling", "v1", "Scale"},
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:392:59: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:413:61: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:415:22: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
expectMatchKind: &schema.GroupVersionKind{"autoscaling", "v1", "Scale"},
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:435:59: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:450:59: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:460:59: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:475:70: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(&example.Pod{}, nil, schema.GroupVersionKind{"example.apiserver.k8s.io", "v1", "Pod"}, "ns", "name", schema.GroupVersionResource{"example.apiserver.k8s.io", "v1", "pods"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:491:70: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(&example.Pod{}, nil, schema.GroupVersionKind{"example.apiserver.k8s.io", "v1", "Pod"}, "ns", "name", schema.GroupVersionResource{"example.apiserver.k8s.io", "v1", "pods"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:507:70: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(&example.Pod{}, nil, schema.GroupVersionKind{"example.apiserver.k8s.io", "v1", "Pod"}, "ns", "name", schema.GroupVersionResource{"example.apiserver.k8s.io", "v1", "pods"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:523:70: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
attrs: admission.NewAttributesRecord(&example.Pod{}, nil, schema.GroupVersionKind{"example.apiserver.k8s.io", "v1", "Pod"}, "ns", "name", schema.GroupVersionResource{"example.apiserver.k8s.io", "v1", "pods"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:591:25: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
mapper.RegisterKindFor(schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "", schema.GroupVersionKind{"extensions", "v1beta1", "Deployment"})
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:592:25: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1", "Deployment"})
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:593:25: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1beta1", "Deployment"})
^
staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching/matching_test.go:594:25: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionResource struct literal uses unkeyed fields (govet)
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1alpha1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1alpha1", "Deployment"})
^
staging/src/k8s.io/client-go/tools/leaderelection/resourcelock/leaselock.go:120:19: composites: k8s.io/apimachinery/pkg/apis/meta/v1.Time struct literal uses unkeyed fields (govet)
r.AcquireTime = metav1.Time{spec.AcquireTime.Time}
^
staging/src/k8s.io/client-go/tools/leaderelection/resourcelock/leaselock.go:123:17: composites: k8s.io/apimachinery/pkg/apis/meta/v1.Time struct literal uses unkeyed fields (govet)
r.RenewTime = metav1.Time{spec.RenewTime.Time}
^
staging/src/k8s.io/client-go/tools/leaderelection/resourcelock/leaselock.go:135:26: composites: k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime struct literal uses unkeyed fields (govet)
AcquireTime: &metav1.MicroTime{ler.AcquireTime.Time},
^
staging/src/k8s.io/client-go/tools/leaderelection/resourcelock/leaselock.go:136:26: composites: k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime struct literal uses unkeyed fields (govet)
RenewTime: &metav1.MicroTime{ler.RenewTime.Time},
^
staging/src/k8s.io/client-go/plugin/pkg/client/auth/exec/exec_test.go:1088:28: composites: k8s.io/apimachinery/pkg/apis/meta/v1.Time struct literal uses unkeyed fields (govet)
ExpirationTimestamp: &v1.Time{now.Add(time.Hour)},
^
staging/src/k8s.io/client-go/plugin/pkg/client/auth/exec/exec_test.go:1100:28: composites: k8s.io/apimachinery/pkg/apis/meta/v1.Time struct literal uses unkeyed fields (govet)
ExpirationTimestamp: &v1.Time{now.Add(time.Hour)},
^
staging/src/k8s.io/client-go/plugin/pkg/client/auth/exec/exec_test.go:1110:28: composites: k8s.io/apimachinery/pkg/apis/meta/v1.Time struct literal uses unkeyed fields (govet)
ExpirationTimestamp: &v1.Time{now.Add(time.Hour)},
^
staging/src/k8s.io/client-go/tools/events/event_recorder.go:44:15: composites: k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime struct literal uses unkeyed fields (govet)
timestamp := metav1.MicroTime{time.Now()}
^
staging/src/k8s.io/client-go/tools/events/eventseries_test.go:95:24: composites: k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime struct literal uses unkeyed fields (govet)
EventTime: metav1.MicroTime{time.Now()},
^
staging/src/k8s.io/client-go/tools/events/eventseries_test.go:299:56: composites: k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime struct literal uses unkeyed fields (govet)
cachedEvent := recorder.makeEvent(regarding, related, metav1.MicroTime{time.Now()}, v1.EventTypeNormal, "test", "some verbose message: 1", "eventTest", "eventTest-"+hostname, "started")
^
staging/src/k8s.io/client-go/tools/events/eventseries_test.go:385:57: composites: k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime struct literal uses unkeyed fields (govet)
cachedEvent := recorder.makeEvent(regarding, related, metav1.MicroTime{time.Now()}, v1.EventTypeNormal, "test", "some verbose message: 1", "eventTest", "eventTest-"+hostname, "started")
^
staging/src/k8s.io/client-go/tools/leaderelection/leaderelection_test.go:365:26: composites: k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime struct literal uses unkeyed fields (govet)
AcquireTime: &metav1.MicroTime{time.Now()},
^
staging/src/k8s.io/client-go/tools/leaderelection/leaderelection_test.go:366:26: composites: k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime struct literal uses unkeyed fields (govet)
RenewTime: &metav1.MicroTime{time.Now()},
^
staging/src/k8s.io/client-go/tools/auth/exec/types_test.go:40:53: loopclosure: loop variable cluster captured by func literal (govet)
testClientAuthenticationClusterTypesAreSynced(t, cluster)
^
staging/src/k8s.io/cli-runtime/pkg/resource/scheme_test.go:44:16: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
expectGVK: &schema.GroupVersionKind{"", "v1", "Status"},
^
staging/src/k8s.io/cli-runtime/pkg/resource/scheme_test.go:50:16: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
expectGVK: &schema.GroupVersionKind{"meta.k8s.io", "v1", "Status"},
^
staging/src/k8s.io/cli-runtime/pkg/resource/scheme_test.go:56:16: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
expectGVK: &schema.GroupVersionKind{"example.com", "v1", "Status"},
^
staging/src/k8s.io/cli-runtime/pkg/resource/scheme_test.go:62:16: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
expectGVK: &schema.GroupVersionKind{"example.com", "v1", "Foo"},
^
staging/src/k8s.io/cli-runtime/pkg/resource/builder_example_test.go:77:1: tests: ExampleLocalBuilder refers to unknown identifier: LocalBuilder (govet)
func ExampleLocalBuilder() {
^
staging/src/k8s.io/component-base/metrics/desc_test.go:159:26: copylocks: call of reflect.DeepEqual copies lock value: k8s.io/component-base/metrics.Desc contains sync.RWMutex (govet)
if !reflect.DeepEqual(*descA, *descB) {
^
staging/src/k8s.io/component-base/logs/json/json_benchmark_test.go:46:6: structtag: struct field secret has json tag but is not exported (govet)
secret string `json:"secret"`
^
staging/src/k8s.io/component-base/logs/json/json_benchmark_test.go:76:6: structtag: struct field secret has json tag but is not exported (govet)
secret string `json:"secret"`
^
staging/src/k8s.io/component-base/logs/json/json_benchmark_test.go:105:6: structtag: struct field secret has json tag but is not exported (govet)
secret string `json:"secret"`
^
staging/src/k8s.io/csi-translation-lib/plugins/vsphere_volume_test.go:31:26: composites: k8s.io/api/core/v1.TopologySelectorTerm struct literal uses unkeyed fields (govet)
topologySelectorTerm := v1.TopologySelectorTerm{[]v1.TopologySelectorLabelRequirement{
^
staging/src/k8s.io/csi-translation-lib/plugins/vsphere_volume_test.go:37:40: composites: k8s.io/api/core/v1.TopologySelectorTerm struct literal uses unkeyed fields (govet)
topologySelectorTermWithBetaLabels := v1.TopologySelectorTerm{[]v1.TopologySelectorLabelRequirement{
^
staging/src/k8s.io/csi-translation-lib/plugins/vsphere_volume_test.go:43:34: composites: k8s.io/api/core/v1.TopologySelectorTerm struct literal uses unkeyed fields (govet)
expectedTopologySelectorTerm := v1.TopologySelectorTerm{[]v1.TopologySelectorLabelRequirement{
^
staging/src/k8s.io/kms/pkg/hierarchy/hierarchy_test.go:506:5: testinggoroutine: call to (*T).Fatalf from a non-test goroutine (govet)
t.Fatalf("Encrypt() error = %v", err)
^
staging/src/k8s.io/kms/pkg/hierarchy/hierarchy_test.go:509:5: testinggoroutine: call to (*T).Fatalf from a non-test goroutine (govet)
t.Fatalf("Encrypt() annotations = %v, want %v", resp.Annotations, encLocalKEK)
^
staging/src/k8s.io/kms/pkg/hierarchy/hierarchy_test.go:539:5: testinggoroutine: call to (*T).Fatalf from a non-test goroutine (govet)
t.Fatalf("Encrypt() error = %v", err)
^
staging/src/k8s.io/kms/pkg/hierarchy/hierarchy_test.go:542:5: testinggoroutine: call to (*T).Fatalf from a non-test goroutine (govet)
t.Fatalf("Encrypt() annotations = %v, want %v", resp.Annotations, lk.encKEK)
^
staging/src/k8s.io/kms/pkg/hierarchy/hierarchy_test.go:589:5: testinggoroutine: call to (*T).Fatalf from a non-test goroutine (govet)
t.Fatalf("Encrypt() error = %v", err)
^
staging/src/k8s.io/kms/pkg/hierarchy/hierarchy_test.go:592:5: testinggoroutine: call to (*T).Fatalf from a non-test goroutine (govet)
t.Fatalf("Encrypt() annotations = %v, want %v", resp.Annotations, encLocalKEK)
^
staging/src/k8s.io/kms/pkg/hierarchy/hierarchy_test.go:627:5: testinggoroutine: call to (*T).Fatalf from a non-test goroutine (govet)
t.Fatalf("Encrypt() error = %v", err)
^
staging/src/k8s.io/kms/pkg/hierarchy/hierarchy_test.go:630:5: testinggoroutine: call to (*T).Fatalf from a non-test goroutine (govet)
t.Fatalf("Encrypt() annotations = %v, want %v", resp.Annotations, lk.encKEK)
^
staging/src/k8s.io/kms/pkg/hierarchy/hierarchy_test.go:677:5: testinggoroutine: call to (*T).Fatalf from a non-test goroutine (govet)
t.Fatalf("Encrypt() error = %v", err)
^
staging/src/k8s.io/kms/pkg/hierarchy/hierarchy_test.go:680:5: testinggoroutine: call to (*T).Fatalf from a non-test goroutine (govet)
t.Fatalf("Encrypt() annotations = %v, want %v", resp.Annotations, encLocalKEK)
^
staging/src/k8s.io/kms/pkg/hierarchy/hierarchy_test.go:717:5: testinggoroutine: call to (*T).Fatalf from a non-test goroutine (govet)
t.Fatalf("Encrypt() error = %v", err)
^
staging/src/k8s.io/kms/pkg/hierarchy/hierarchy_test.go:720:5: testinggoroutine: call to (*T).Fatalf from a non-test goroutine (govet)
t.Fatalf("Encrypt() annotations = %v, want %v", resp.Annotations, lk.encKEK)
^
staging/src/k8s.io/kubectl/pkg/cmd/debug/debug_test.go:451:25: composites: k8s.io/apimachinery/pkg/apis/meta/v1.Time struct literal uses unkeyed fields (govet)
CreationTimestamp: metav1.Time{time.Now()},
^
staging/src/k8s.io/kubectl/pkg/cmd/util/helpers_test.go:341:5: composites: k8s.io/apimachinery/pkg/api/errors.StatusError struct literal uses unkeyed fields (govet)
&errors.StatusError{metav1.Status{
^
staging/src/k8s.io/kubectl/pkg/cmd/util/helpers_test.go:352:5: composites: k8s.io/apimachinery/pkg/api/errors.StatusError struct literal uses unkeyed fields (govet)
&errors.StatusError{metav1.Status{
^
staging/src/k8s.io/kubectl/pkg/cmd/util/helpers_test.go:364:5: composites: k8s.io/apimachinery/pkg/api/errors.StatusError struct literal uses unkeyed fields (govet)
&errors.StatusError{metav1.Status{
^
staging/src/k8s.io/kubectl/pkg/cmd/util/helpers_test.go:374:5: composites: k8s.io/apimachinery/pkg/api/errors.StatusError struct literal uses unkeyed fields (govet)
&errors.StatusError{metav1.Status{
^
staging/src/k8s.io/kubectl/pkg/cmd/util/helpers_test.go:385:50: composites: k8s.io/apimachinery/pkg/api/errors.StatusError struct literal uses unkeyed fields (govet)
AddSourceToErr("creating", "configmap.yaml", &errors.StatusError{metav1.Status{
^
staging/src/k8s.io/kubectl/pkg/cmd/util/helpers_test.go:395:5: composites: k8s.io/apimachinery/pkg/api/errors.StatusError struct literal uses unkeyed fields (govet)
&errors.StatusError{metav1.Status{
^
staging/src/k8s.io/kubectl/pkg/explain/v2/funcs_test.go:204:15: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
"needle": schema.GroupVersionKind{"testgroup.k8s.io", "v1", "Kind"},
^
staging/src/k8s.io/kubectl/pkg/explain/v2/funcs_test.go:206:6: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
{"randomgroup.k8s.io", "v1", "OtherKind"},
^
staging/src/k8s.io/kubectl/pkg/explain/v2/funcs_test.go:207:6: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
{"testgroup.k8s.io", "v1", "OtherKind"},
^
staging/src/k8s.io/kubectl/pkg/explain/v2/funcs_test.go:208:6: composites: k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind struct literal uses unkeyed fields (govet)
{"testgroup.k8s.io", "v1", "Kind"},
^
staging/src/k8s.io/kubectl/pkg/polymorphichelpers/history_test.go:95:46: composites: k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference struct literal uses unkeyed fields (govet)
OwnerReferences: []metav1.OwnerReference{{"apps/v1", "Deployment", deployment.Name, deployment.UID, &trueVar, nil}},
^
staging/src/k8s.io/kubectl/pkg/polymorphichelpers/history_test.go:222:46: composites: k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference struct literal uses unkeyed fields (govet)
OwnerReferences: []metav1.OwnerReference{{"apps/v1", "StatefulSet", "moons", "1993", &trueVar, nil}},
^
staging/src/k8s.io/kubectl/pkg/polymorphichelpers/history_test.go:326:46: composites: k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference struct literal uses unkeyed fields (govet)
OwnerReferences: []metav1.OwnerReference{{"apps/v1", "DaemonSet", "moons", "1993", &trueVar, nil}},
^
staging/src/k8s.io/kubectl/pkg/util/i18n/i18n_test.go:143:31: loopclosure: loop variable envVar captured by func literal (govet)
defer func() { os.Setenv(envVar, envVarValue) }()
^
staging/src/k8s.io/legacy-cloud-providers/aws/aws_assumerole_provider_test.go:95:9: copylocks: range var tt copies lock: struct{name string; fields k8s.io/legacy-cloud-providers/aws.fields; want github.com/aws/aws-sdk-go/aws/credentials.Value; wantProviderCalled bool; sleepBeforeCallingProvider time.Duration; wantErr bool; wantErrString string} contains k8s.io/legacy-cloud-providers/aws.fields contains sync.RWMutex (govet)
for _, tt := range tests {
^
staging/src/k8s.io/legacy-cloud-providers/vsphere/nodemanager.go:190:5: lostcancel: the cancel function is not used on all paths (possible context leak) (govet)
ctx, cancel := context.WithCancel(context.Background())
^
staging/src/k8s.io/legacy-cloud-providers/vsphere/nodemanager.go:236:3: lostcancel: this return statement may be reached without using the cancel var defined on line 190 (govet)
}()
^
staging/src/k8s.io/pod-security-admission/policy/registry_test.go:152:35: composites: k8s.io/pod-security-admission/api.LevelVersion struct literal uses unkeyed fields (govet)
results := registry.EvaluatePod(api.LevelVersion{tc.level, versionOrPanic(tc.version)}, nil, nil)
^
staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/fischer/etcd.go:50:10: composites: k8s.io/sample-apiserver/pkg/registry.REST struct literal uses unkeyed fields (govet)
return ®istry.REST{store}, nil
^
staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/flunder/etcd.go:50:10: composites: k8s.io/sample-apiserver/pkg/registry.REST struct literal uses unkeyed fields (govet)
return ®istry.REST{store}, nil
^
Kubernetes-commit: a58eb1b3da870b2b568fcf0ffd42332d6a0fd667
1265 lines
34 KiB
Go
1265 lines
34 KiB
Go
/*
|
|
Copyright 2018 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package exec
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/client-go/pkg/apis/clientauthentication"
|
|
"k8s.io/client-go/tools/clientcmd/api"
|
|
"k8s.io/client-go/transport"
|
|
testingclock "k8s.io/utils/clock/testing"
|
|
)
|
|
|
|
var (
|
|
certData = []byte(`-----BEGIN CERTIFICATE-----
|
|
MIIC6jCCAdSgAwIBAgIBCzALBgkqhkiG9w0BAQswIzEhMB8GA1UEAwwYMTAuMTMu
|
|
MTI5LjEwNkAxNDIxMzU5MDU4MB4XDTE1MDExNTIyMDEzMVoXDTE2MDExNTIyMDEz
|
|
MlowGzEZMBcGA1UEAxMQb3BlbnNoaWZ0LWNsaWVudDCCASIwDQYJKoZIhvcNAQEB
|
|
BQADggEPADCCAQoCggEBAKtdhz0+uCLXw5cSYns9rU/XifFSpb/x24WDdrm72S/v
|
|
b9BPYsAStiP148buylr1SOuNi8sTAZmlVDDIpIVwMLff+o2rKYDicn9fjbrTxTOj
|
|
lI4pHJBH+JU3AJ0tbajupioh70jwFS0oYpwtneg2zcnE2Z4l6mhrj2okrc5Q1/X2
|
|
I2HChtIU4JYTisObtin10QKJX01CLfYXJLa8upWzKZ4/GOcHG+eAV3jXWoXidtjb
|
|
1Usw70amoTZ6mIVCkiu1QwCoa8+ycojGfZhvqMsAp1536ZcCul+Na+AbCv4zKS7F
|
|
kQQaImVrXdUiFansIoofGlw/JNuoKK6ssVpS5Ic3pgcCAwEAAaM1MDMwDgYDVR0P
|
|
AQH/BAQDAgCgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwCwYJ
|
|
KoZIhvcNAQELA4IBAQCKLREH7bXtXtZ+8vI6cjD7W3QikiArGqbl36bAhhWsJLp/
|
|
p/ndKz39iFNaiZ3GlwIURWOOKx3y3GA0x9m8FR+Llthf0EQ8sUjnwaknWs0Y6DQ3
|
|
jjPFZOpV3KPCFrdMJ3++E3MgwFC/Ih/N2ebFX9EcV9Vcc6oVWMdwT0fsrhu683rq
|
|
6GSR/3iVX1G/pmOiuaR0fNUaCyCfYrnI4zHBDgSfnlm3vIvN2lrsR/DQBakNL8DJ
|
|
HBgKxMGeUPoneBv+c8DMXIL0EhaFXRlBv9QW45/GiAIOuyFJ0i6hCtGZpJjq4OpQ
|
|
BRjCI+izPzFTjsxD4aORE+WOkyWFCGPWKfNejfw0
|
|
-----END CERTIFICATE-----`)
|
|
keyData = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
|
MIIEowIBAAKCAQEAq12HPT64ItfDlxJiez2tT9eJ8VKlv/HbhYN2ubvZL+9v0E9i
|
|
wBK2I/Xjxu7KWvVI642LyxMBmaVUMMikhXAwt9/6jaspgOJyf1+NutPFM6OUjikc
|
|
kEf4lTcAnS1tqO6mKiHvSPAVLShinC2d6DbNycTZniXqaGuPaiStzlDX9fYjYcKG
|
|
0hTglhOKw5u2KfXRAolfTUIt9hcktry6lbMpnj8Y5wcb54BXeNdaheJ22NvVSzDv
|
|
RqahNnqYhUKSK7VDAKhrz7JyiMZ9mG+oywCnXnfplwK6X41r4BsK/jMpLsWRBBoi
|
|
ZWtd1SIVqewiih8aXD8k26gorqyxWlLkhzemBwIDAQABAoIBAD2XYRs3JrGHQUpU
|
|
FkdbVKZkvrSY0vAZOqBTLuH0zUv4UATb8487anGkWBjRDLQCgxH+jucPTrztekQK
|
|
aW94clo0S3aNtV4YhbSYIHWs1a0It0UdK6ID7CmdWkAj6s0T8W8lQT7C46mWYVLm
|
|
5mFnCTHi6aB42jZrqmEpC7sivWwuU0xqj3Ml8kkxQCGmyc9JjmCB4OrFFC8NNt6M
|
|
ObvQkUI6Z3nO4phTbpxkE1/9dT0MmPIF7GhHVzJMS+EyyRYUDllZ0wvVSOM3qZT0
|
|
JMUaBerkNwm9foKJ1+dv2nMKZZbJajv7suUDCfU44mVeaEO+4kmTKSGCGjjTBGkr
|
|
7L1ySDECgYEA5ElIMhpdBzIivCuBIH8LlUeuzd93pqssO1G2Xg0jHtfM4tz7fyeI
|
|
cr90dc8gpli24dkSxzLeg3Tn3wIj/Bu64m2TpZPZEIlukYvgdgArmRIPQVxerYey
|
|
OkrfTNkxU1HXsYjLCdGcGXs5lmb+K/kuTcFxaMOs7jZi7La+jEONwf8CgYEAwCs/
|
|
rUOOA0klDsWWisbivOiNPII79c9McZCNBqncCBfMUoiGe8uWDEO4TFHN60vFuVk9
|
|
8PkwpCfvaBUX+ajvbafIfHxsnfk1M04WLGCeqQ/ym5Q4sQoQOcC1b1y9qc/xEWfg
|
|
nIUuia0ukYRpl7qQa3tNg+BNFyjypW8zukUAC/kCgYB1/Kojuxx5q5/oQVPrx73k
|
|
2bevD+B3c+DYh9MJqSCNwFtUpYIWpggPxoQan4LwdsmO0PKzocb/ilyNFj4i/vII
|
|
NToqSc/WjDFpaDIKyuu9oWfhECye45NqLWhb/6VOuu4QA/Nsj7luMhIBehnEAHW+
|
|
GkzTKM8oD1PxpEG3nPKXYQKBgQC6AuMPRt3XBl1NkCrpSBy/uObFlFaP2Enpf39S
|
|
3OZ0Gv0XQrnSaL1kP8TMcz68rMrGX8DaWYsgytstR4W+jyy7WvZwsUu+GjTJ5aMG
|
|
77uEcEBpIi9CBzivfn7hPccE8ZgqPf+n4i6q66yxBJflW5xhvafJqDtW2LcPNbW/
|
|
bvzdmQKBgExALRUXpq+5dbmkdXBHtvXdRDZ6rVmrnjy4nI5bPw+1GqQqk6uAR6B/
|
|
F6NmLCQOO4PDG/cuatNHIr2FrwTmGdEL6ObLUGWn9Oer9gJhHVqqsY5I4sEPo4XX
|
|
stR0Yiw0buV6DL/moUO0HIM9Bjh96HJp+LxiIS6UCdIhMPp5HoQa
|
|
-----END RSA PRIVATE KEY-----`)
|
|
validCert *tls.Certificate
|
|
)
|
|
|
|
func init() {
|
|
cert, err := tls.X509KeyPair(certData, keyData)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
validCert = &cert
|
|
}
|
|
|
|
func TestCacheKey(t *testing.T) {
|
|
c1 := &api.ExecConfig{
|
|
Command: "foo-bar",
|
|
Args: []string{"1", "2"},
|
|
Env: []api.ExecEnvVar{
|
|
{Name: "3", Value: "4"},
|
|
{Name: "5", Value: "6"},
|
|
{Name: "7", Value: "8"},
|
|
},
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
ProvideClusterInfo: true,
|
|
}
|
|
c1c := &clientauthentication.Cluster{
|
|
Server: "foo",
|
|
TLSServerName: "bar",
|
|
CertificateAuthorityData: []byte("baz"),
|
|
Config: &runtime.Unknown{
|
|
TypeMeta: runtime.TypeMeta{
|
|
APIVersion: "",
|
|
Kind: "",
|
|
},
|
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"snorlax"}}`),
|
|
ContentEncoding: "",
|
|
ContentType: "application/json",
|
|
},
|
|
}
|
|
|
|
c2 := &api.ExecConfig{
|
|
Command: "foo-bar",
|
|
Args: []string{"1", "2"},
|
|
Env: []api.ExecEnvVar{
|
|
{Name: "3", Value: "4"},
|
|
{Name: "5", Value: "6"},
|
|
{Name: "7", Value: "8"},
|
|
},
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
ProvideClusterInfo: true,
|
|
}
|
|
c2c := &clientauthentication.Cluster{
|
|
Server: "foo",
|
|
TLSServerName: "bar",
|
|
CertificateAuthorityData: []byte("baz"),
|
|
Config: &runtime.Unknown{
|
|
TypeMeta: runtime.TypeMeta{
|
|
APIVersion: "",
|
|
Kind: "",
|
|
},
|
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"snorlax"}}`),
|
|
ContentEncoding: "",
|
|
ContentType: "application/json",
|
|
},
|
|
}
|
|
|
|
c3 := &api.ExecConfig{
|
|
Command: "foo-bar",
|
|
Args: []string{"1", "2"},
|
|
Env: []api.ExecEnvVar{
|
|
{Name: "3", Value: "4"},
|
|
{Name: "5", Value: "6"},
|
|
},
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
}
|
|
c3c := &clientauthentication.Cluster{
|
|
Server: "foo",
|
|
TLSServerName: "bar",
|
|
CertificateAuthorityData: []byte("baz"),
|
|
Config: &runtime.Unknown{
|
|
TypeMeta: runtime.TypeMeta{
|
|
APIVersion: "",
|
|
Kind: "",
|
|
},
|
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"snorlax"}}`),
|
|
ContentEncoding: "",
|
|
ContentType: "application/json",
|
|
},
|
|
}
|
|
|
|
c4 := &api.ExecConfig{
|
|
Command: "foo-bar",
|
|
Args: []string{"1", "2"},
|
|
Env: []api.ExecEnvVar{
|
|
{Name: "3", Value: "4"},
|
|
{Name: "5", Value: "6"},
|
|
},
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
}
|
|
c4c := &clientauthentication.Cluster{
|
|
Server: "foo",
|
|
TLSServerName: "bar",
|
|
CertificateAuthorityData: []byte("baz"),
|
|
Config: &runtime.Unknown{
|
|
TypeMeta: runtime.TypeMeta{
|
|
APIVersion: "",
|
|
Kind: "",
|
|
},
|
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"panda"}}`),
|
|
ContentEncoding: "",
|
|
ContentType: "application/json",
|
|
},
|
|
}
|
|
|
|
// c5/c5c should be the same as c4/c4c, except c5 has ProvideClusterInfo set to true.
|
|
c5 := &api.ExecConfig{
|
|
Command: "foo-bar",
|
|
Args: []string{"1", "2"},
|
|
Env: []api.ExecEnvVar{
|
|
{Name: "3", Value: "4"},
|
|
{Name: "5", Value: "6"},
|
|
},
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
ProvideClusterInfo: true,
|
|
}
|
|
c5c := &clientauthentication.Cluster{
|
|
Server: "foo",
|
|
TLSServerName: "bar",
|
|
CertificateAuthorityData: []byte("baz"),
|
|
Config: &runtime.Unknown{
|
|
TypeMeta: runtime.TypeMeta{
|
|
APIVersion: "",
|
|
Kind: "",
|
|
},
|
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"panda"}}`),
|
|
ContentEncoding: "",
|
|
ContentType: "application/json",
|
|
},
|
|
}
|
|
|
|
// c6 should be the same as c4, except c6 is passed with a nil cluster
|
|
c6 := &api.ExecConfig{
|
|
Command: "foo-bar",
|
|
Args: []string{"1", "2"},
|
|
Env: []api.ExecEnvVar{
|
|
{Name: "3", Value: "4"},
|
|
{Name: "5", Value: "6"},
|
|
},
|
|
APIVersion: "client.authentication.k8s.io/v1betaa1",
|
|
}
|
|
|
|
// c7 should be the same as c6, except c7 has stdin marked as unavailable
|
|
c7 := &api.ExecConfig{
|
|
Command: "foo-bar",
|
|
Args: []string{"1", "2"},
|
|
Env: []api.ExecEnvVar{
|
|
{Name: "3", Value: "4"},
|
|
{Name: "5", Value: "6"},
|
|
},
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
StdinUnavailable: true,
|
|
}
|
|
|
|
key1 := cacheKey(c1, c1c)
|
|
key2 := cacheKey(c2, c2c)
|
|
key3 := cacheKey(c3, c3c)
|
|
key4 := cacheKey(c4, c4c)
|
|
key5 := cacheKey(c5, c5c)
|
|
key6 := cacheKey(c6, nil)
|
|
key7 := cacheKey(c7, nil)
|
|
if key1 != key2 {
|
|
t.Error("key1 and key2 didn't match")
|
|
}
|
|
if key1 == key3 {
|
|
t.Error("key1 and key3 matched")
|
|
}
|
|
if key2 == key3 {
|
|
t.Error("key2 and key3 matched")
|
|
}
|
|
if key3 == key4 {
|
|
t.Error("key3 and key4 matched")
|
|
}
|
|
if key4 == key5 {
|
|
t.Error("key3 and key4 matched")
|
|
}
|
|
if key6 == key4 {
|
|
t.Error("key6 and key4 matched")
|
|
}
|
|
if key6 == key7 {
|
|
t.Error("key6 and key7 matched")
|
|
}
|
|
}
|
|
|
|
func compJSON(t *testing.T, got, want []byte) {
|
|
t.Helper()
|
|
gotJSON := &bytes.Buffer{}
|
|
wantJSON := &bytes.Buffer{}
|
|
|
|
if err := json.Indent(gotJSON, got, "", " "); err != nil {
|
|
t.Errorf("got invalid JSON: %v", err)
|
|
}
|
|
if err := json.Indent(wantJSON, want, "", " "); err != nil {
|
|
t.Errorf("want invalid JSON: %v", err)
|
|
}
|
|
g := strings.TrimSpace(gotJSON.String())
|
|
w := strings.TrimSpace(wantJSON.String())
|
|
if g != w {
|
|
t.Errorf("wanted %q, got %q", w, g)
|
|
}
|
|
}
|
|
|
|
func TestRefreshCreds(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
config api.ExecConfig
|
|
stdinUnavailable bool
|
|
exitCode int
|
|
cluster *clientauthentication.Cluster
|
|
output string
|
|
isTerminal bool
|
|
wantInput string
|
|
wantCreds credentials
|
|
wantExpiry time.Time
|
|
wantErr bool
|
|
wantErrSubstr string
|
|
}{
|
|
{
|
|
name: "beta-with-TLS-credentials",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
wantInput: `{
|
|
"kind":"ExecCredential",
|
|
"apiVersion":"client.authentication.k8s.io/v1beta1",
|
|
"spec": {
|
|
"interactive": false
|
|
}
|
|
}`,
|
|
output: fmt.Sprintf(`{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"clientKeyData": %q,
|
|
"clientCertificateData": %q
|
|
}
|
|
}`, keyData, certData),
|
|
wantCreds: credentials{cert: validCert},
|
|
},
|
|
{
|
|
name: "beta-with-bad-TLS-credentials",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"clientKeyData": "foo",
|
|
"clientCertificateData": "bar"
|
|
}
|
|
}`,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "beta-cert-but-no-key",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
output: fmt.Sprintf(`{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"clientCertificateData": %q
|
|
}
|
|
}`, certData),
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "beta-basic-request",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
wantInput: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"spec": {
|
|
"interactive": false
|
|
}
|
|
}`,
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "foo-bar"
|
|
}
|
|
}`,
|
|
wantCreds: credentials{token: "foo-bar"},
|
|
},
|
|
{
|
|
name: "beta-basic-request-with-never-interactive-mode",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.NeverExecInteractiveMode,
|
|
},
|
|
wantInput: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"spec": {
|
|
"interactive": false
|
|
}
|
|
}`,
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "foo-bar"
|
|
}
|
|
}`,
|
|
wantCreds: credentials{token: "foo-bar"},
|
|
},
|
|
{
|
|
name: "beta-basic-request-with-never-interactive-mode-and-stdin-unavailable",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.NeverExecInteractiveMode,
|
|
StdinUnavailable: true,
|
|
},
|
|
wantInput: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"spec": {
|
|
"interactive": false
|
|
}
|
|
}`,
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "foo-bar"
|
|
}
|
|
}`,
|
|
wantCreds: credentials{token: "foo-bar"},
|
|
},
|
|
{
|
|
name: "beta-basic-request-with-if-available-interactive-mode",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
wantInput: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"spec": {
|
|
"interactive": false
|
|
}
|
|
}`,
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "foo-bar"
|
|
}
|
|
}`,
|
|
wantCreds: credentials{token: "foo-bar"},
|
|
},
|
|
{
|
|
name: "beta-basic-request-with-if-available-interactive-mode-and-stdin-unavailable",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
StdinUnavailable: true,
|
|
},
|
|
wantInput: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"spec": {
|
|
"interactive": false
|
|
}
|
|
}`,
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "foo-bar"
|
|
}
|
|
}`,
|
|
wantCreds: credentials{token: "foo-bar"},
|
|
},
|
|
{
|
|
name: "beta-basic-request-with-if-available-interactive-mode-and-terminal",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
isTerminal: true,
|
|
wantInput: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"spec": {
|
|
"interactive": true
|
|
}
|
|
}`,
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "foo-bar"
|
|
}
|
|
}`,
|
|
wantCreds: credentials{token: "foo-bar"},
|
|
},
|
|
{
|
|
name: "beta-basic-request-with-if-available-interactive-mode-and-terminal-and-stdin-unavailable",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
StdinUnavailable: true,
|
|
},
|
|
isTerminal: true,
|
|
wantInput: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"spec": {
|
|
"interactive": false
|
|
}
|
|
}`,
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "foo-bar"
|
|
}
|
|
}`,
|
|
wantCreds: credentials{token: "foo-bar"},
|
|
},
|
|
{
|
|
name: "beta-basic-request-with-always-interactive-mode",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.AlwaysExecInteractiveMode,
|
|
},
|
|
wantErr: true,
|
|
wantErrSubstr: "exec plugin cannot support interactive mode: standard input is not a terminal",
|
|
},
|
|
{
|
|
name: "beta-basic-request-with-always-interactive-mode-and-terminal-and-stdin-unavailable",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.AlwaysExecInteractiveMode,
|
|
StdinUnavailable: true,
|
|
},
|
|
isTerminal: true,
|
|
wantErr: true,
|
|
wantErrSubstr: "exec plugin cannot support interactive mode: standard input is unavailable",
|
|
},
|
|
{
|
|
name: "beta-basic-request-with-always-interactive-mode-and-terminal-and-stdin-unavailable-with-message",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.AlwaysExecInteractiveMode,
|
|
StdinUnavailable: true,
|
|
StdinUnavailableMessage: "some message",
|
|
},
|
|
isTerminal: true,
|
|
wantErr: true,
|
|
wantErrSubstr: "exec plugin cannot support interactive mode: standard input is unavailable: some message",
|
|
},
|
|
{
|
|
name: "beta-basic-request-with-always-interactive-mode-and-terminal",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.AlwaysExecInteractiveMode,
|
|
},
|
|
isTerminal: true,
|
|
wantInput: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"spec": {
|
|
"interactive": true
|
|
}
|
|
}`,
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "foo-bar"
|
|
}
|
|
}`,
|
|
wantCreds: credentials{token: "foo-bar"},
|
|
},
|
|
{
|
|
name: "beta-expiry",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
wantInput: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"spec": {
|
|
"interactive": false
|
|
}
|
|
}`,
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "foo-bar",
|
|
"expirationTimestamp": "2006-01-02T15:04:05Z"
|
|
}
|
|
}`,
|
|
wantExpiry: time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC),
|
|
wantCreds: credentials{token: "foo-bar"},
|
|
},
|
|
{
|
|
name: "beta-no-group-version",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"status": {
|
|
"token": "foo-bar"
|
|
}
|
|
}`,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "beta-no-status",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion":"client.authentication.k8s.io/v1beta1"
|
|
}`,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "beta-no-token",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion":"client.authentication.k8s.io/v1beta1",
|
|
"status": {}
|
|
}`,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "unknown-binary",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
Command: "does not exist",
|
|
InstallHint: "some install hint",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
wantErr: true,
|
|
wantErrSubstr: "some install hint",
|
|
},
|
|
{
|
|
name: "binary-fails",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
exitCode: 73,
|
|
wantErr: true,
|
|
wantErrSubstr: "73",
|
|
},
|
|
{
|
|
name: "beta-with-cluster-and-provide-cluster-info-is-serialized",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
ProvideClusterInfo: true,
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
cluster: &clientauthentication.Cluster{
|
|
Server: "foo",
|
|
TLSServerName: "bar",
|
|
CertificateAuthorityData: []byte("baz"),
|
|
Config: &runtime.Unknown{
|
|
TypeMeta: runtime.TypeMeta{
|
|
APIVersion: "",
|
|
Kind: "",
|
|
},
|
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"snorlax"}}`),
|
|
ContentEncoding: "",
|
|
ContentType: "application/json",
|
|
},
|
|
},
|
|
wantInput: `{
|
|
"kind":"ExecCredential",
|
|
"apiVersion":"client.authentication.k8s.io/v1beta1",
|
|
"spec": {
|
|
"cluster": {
|
|
"server": "foo",
|
|
"tls-server-name": "bar",
|
|
"certificate-authority-data": "YmF6",
|
|
"config": {
|
|
"apiVersion": "group/v1",
|
|
"kind": "PluginConfig",
|
|
"spec": {
|
|
"audience": "snorlax"
|
|
}
|
|
}
|
|
},
|
|
"interactive": false
|
|
}
|
|
}`,
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "foo-bar"
|
|
}
|
|
}`,
|
|
wantCreds: credentials{token: "foo-bar"},
|
|
},
|
|
{
|
|
name: "beta-with-cluster-and-without-provide-cluster-info-is-not-serialized",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
cluster: &clientauthentication.Cluster{
|
|
Server: "foo",
|
|
TLSServerName: "bar",
|
|
CertificateAuthorityData: []byte("baz"),
|
|
Config: &runtime.Unknown{
|
|
TypeMeta: runtime.TypeMeta{
|
|
APIVersion: "",
|
|
Kind: "",
|
|
},
|
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"snorlax"}}`),
|
|
ContentEncoding: "",
|
|
ContentType: "application/json",
|
|
},
|
|
},
|
|
wantInput: `{
|
|
"kind":"ExecCredential",
|
|
"apiVersion":"client.authentication.k8s.io/v1beta1",
|
|
"spec": {
|
|
"interactive": false
|
|
}
|
|
}`,
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "foo-bar"
|
|
}
|
|
}`,
|
|
wantCreds: credentials{token: "foo-bar"},
|
|
},
|
|
{
|
|
name: "v1-basic-request",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
},
|
|
wantInput: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1",
|
|
"spec": {
|
|
"interactive": false
|
|
}
|
|
}`,
|
|
output: `{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1",
|
|
"status": {
|
|
"token": "foo-bar"
|
|
}
|
|
}`,
|
|
wantCreds: credentials{token: "foo-bar"},
|
|
},
|
|
{
|
|
name: "v1-with-missing-interactive-mode",
|
|
config: api.ExecConfig{
|
|
APIVersion: "client.authentication.k8s.io/v1",
|
|
},
|
|
wantErr: true,
|
|
wantErrSubstr: `exec plugin cannot support interactive mode: unknown interactiveMode: ""`,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
c := test.config
|
|
|
|
if c.Command == "" {
|
|
c.Command = "./testdata/test-plugin.sh"
|
|
c.Env = append(c.Env, api.ExecEnvVar{
|
|
Name: "TEST_OUTPUT",
|
|
Value: test.output,
|
|
})
|
|
c.Env = append(c.Env, api.ExecEnvVar{
|
|
Name: "TEST_EXIT_CODE",
|
|
Value: strconv.Itoa(test.exitCode),
|
|
})
|
|
}
|
|
|
|
a, err := newAuthenticator(newCache(), func(_ int) bool { return test.isTerminal }, &c, test.cluster)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
stderr := &bytes.Buffer{}
|
|
a.stderr = stderr
|
|
a.environ = func() []string { return nil }
|
|
|
|
if err := a.refreshCredsLocked(); err != nil {
|
|
if !test.wantErr {
|
|
t.Errorf("get token %v", err)
|
|
} else if !strings.Contains(err.Error(), test.wantErrSubstr) {
|
|
t.Errorf("expected error with substring '%v' got '%v'", test.wantErrSubstr, err.Error())
|
|
}
|
|
return
|
|
}
|
|
if test.wantErr {
|
|
t.Fatal("expected error getting token")
|
|
}
|
|
|
|
if !reflect.DeepEqual(a.cachedCreds, &test.wantCreds) {
|
|
t.Errorf("expected credentials %+v got %+v", &test.wantCreds, a.cachedCreds)
|
|
}
|
|
|
|
if !a.exp.Equal(test.wantExpiry) {
|
|
t.Errorf("expected expiry %v got %v", test.wantExpiry, a.exp)
|
|
}
|
|
|
|
if test.wantInput == "" {
|
|
if got := strings.TrimSpace(stderr.String()); got != "" {
|
|
t.Errorf("expected no input parameters, got %q", got)
|
|
}
|
|
return
|
|
}
|
|
|
|
compJSON(t, stderr.Bytes(), []byte(test.wantInput))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRoundTripper(t *testing.T) {
|
|
wantToken := ""
|
|
|
|
n := time.Now()
|
|
now := func() time.Time { return n }
|
|
|
|
env := []string{""}
|
|
environ := func() []string {
|
|
s := make([]string, len(env))
|
|
copy(s, env)
|
|
return s
|
|
}
|
|
|
|
setOutput := func(s string) {
|
|
env[0] = "TEST_OUTPUT=" + s
|
|
}
|
|
|
|
handler := func(w http.ResponseWriter, r *http.Request) {
|
|
gotToken := ""
|
|
parts := strings.Split(r.Header.Get("Authorization"), " ")
|
|
if len(parts) > 1 && strings.EqualFold(parts[0], "bearer") {
|
|
gotToken = parts[1]
|
|
}
|
|
|
|
if wantToken != gotToken {
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
fmt.Fprintln(w, "ok")
|
|
}
|
|
server := httptest.NewServer(http.HandlerFunc(handler))
|
|
|
|
c := api.ExecConfig{
|
|
Command: "./testdata/test-plugin.sh",
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
}
|
|
a, err := newAuthenticator(newCache(), func(_ int) bool { return false }, &c, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
a.environ = environ
|
|
a.now = now
|
|
a.stderr = io.Discard
|
|
|
|
tc := &transport.Config{}
|
|
if err := a.UpdateTransportConfig(tc); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
client := http.Client{
|
|
Transport: tc.WrapTransport(http.DefaultTransport),
|
|
}
|
|
|
|
get := func(t *testing.T, statusCode int) {
|
|
t.Helper()
|
|
resp, err := client.Get(server.URL)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != statusCode {
|
|
t.Errorf("wanted status %d got %d", statusCode, resp.StatusCode)
|
|
}
|
|
}
|
|
|
|
setOutput(`{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "token1"
|
|
}
|
|
}`)
|
|
wantToken = "token1"
|
|
get(t, http.StatusOK)
|
|
|
|
setOutput(`{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "token2"
|
|
}
|
|
}`)
|
|
// Previous token should be cached
|
|
get(t, http.StatusOK)
|
|
|
|
wantToken = "token2"
|
|
// Token is still cached, hits unauthorized but causes token to rotate.
|
|
get(t, http.StatusUnauthorized)
|
|
// Follow up request uses the rotated token.
|
|
get(t, http.StatusOK)
|
|
|
|
setOutput(`{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "token3",
|
|
"expirationTimestamp": "` + now().Add(time.Hour).Format(time.RFC3339Nano) + `"
|
|
}
|
|
}`)
|
|
wantToken = "token3"
|
|
// Token is still cached, hit's unauthorized but causes rotation to token with an expiry.
|
|
get(t, http.StatusUnauthorized)
|
|
get(t, http.StatusOK)
|
|
|
|
// Move time forward 2 hours, "token3" is now expired.
|
|
n = n.Add(time.Hour * 2)
|
|
setOutput(`{
|
|
"kind": "ExecCredential",
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
"status": {
|
|
"token": "token4",
|
|
"expirationTimestamp": "` + now().Add(time.Hour).Format(time.RFC3339Nano) + `"
|
|
}
|
|
}`)
|
|
wantToken = "token4"
|
|
// Old token is expired, should refresh automatically without hitting a 401.
|
|
get(t, http.StatusOK)
|
|
}
|
|
|
|
func TestAuthorizationHeaderPresentCancelsExecAction(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setTransportConfig func(*transport.Config)
|
|
}{
|
|
{
|
|
name: "bearer token",
|
|
setTransportConfig: func(config *transport.Config) {
|
|
config.BearerToken = "token1f"
|
|
},
|
|
},
|
|
{
|
|
name: "basic auth",
|
|
setTransportConfig: func(config *transport.Config) {
|
|
config.Username = "marshmallow"
|
|
config.Password = "zelda"
|
|
},
|
|
},
|
|
{
|
|
name: "cert auth",
|
|
setTransportConfig: func(config *transport.Config) {
|
|
config.TLS.CertData = []byte("some-cert-data")
|
|
config.TLS.KeyData = []byte("some-key-data")
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
a, err := newAuthenticator(newCache(), func(_ int) bool { return false }, &api.ExecConfig{
|
|
Command: "./testdata/test-plugin.sh",
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// UpdateTransportConfig returns error on existing TLS certificate callback, unless a bearer token is present in the
|
|
// transport config, in which case it takes precedence
|
|
cert := func() (*tls.Certificate, error) {
|
|
return nil, nil
|
|
}
|
|
tc := &transport.Config{TLS: transport.TLSConfig{Insecure: true, GetCertHolder: &transport.GetCertHolder{GetCert: cert}}}
|
|
test.setTransportConfig(tc)
|
|
|
|
if err := a.UpdateTransportConfig(tc); err != nil {
|
|
t.Error("Expected presence of bearer token in config to cancel exec action")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTLSCredentials(t *testing.T) {
|
|
now := time.Now()
|
|
|
|
certPool := x509.NewCertPool()
|
|
cert, key := genClientCert(t)
|
|
if !certPool.AppendCertsFromPEM(cert) {
|
|
t.Fatal("failed to add client cert to CertPool")
|
|
}
|
|
|
|
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
fmt.Fprintln(w, "ok")
|
|
}))
|
|
server.TLS = &tls.Config{
|
|
ClientAuth: tls.RequireAndVerifyClientCert,
|
|
ClientCAs: certPool,
|
|
}
|
|
server.StartTLS()
|
|
defer server.Close()
|
|
|
|
a, err := newAuthenticator(newCache(), func(_ int) bool { return false }, &api.ExecConfig{
|
|
Command: "./testdata/test-plugin.sh",
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var output *clientauthentication.ExecCredential
|
|
a.environ = func() []string {
|
|
data, err := runtime.Encode(codecs.LegacyCodec(a.group), output)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return []string{"TEST_OUTPUT=" + string(data)}
|
|
}
|
|
a.now = func() time.Time { return now }
|
|
a.stderr = io.Discard
|
|
|
|
// We're not interested in server's cert, this test is about client cert.
|
|
tc := &transport.Config{TLS: transport.TLSConfig{Insecure: true}}
|
|
if err := a.UpdateTransportConfig(tc); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
get := func(t *testing.T, desc string, wantErr bool) {
|
|
t.Run(desc, func(t *testing.T) {
|
|
tlsCfg, err := transport.TLSConfigFor(tc)
|
|
if err != nil {
|
|
t.Fatal("TLSConfigFor:", err)
|
|
}
|
|
client := http.Client{
|
|
Transport: &http.Transport{TLSClientConfig: tlsCfg},
|
|
}
|
|
resp, err := client.Get(server.URL)
|
|
switch {
|
|
case err != nil && !wantErr:
|
|
t.Errorf("got client.Get error: %q, want nil", err)
|
|
case err == nil && wantErr:
|
|
t.Error("got nil client.Get error, want non-nil")
|
|
}
|
|
if err == nil {
|
|
resp.Body.Close()
|
|
}
|
|
})
|
|
}
|
|
|
|
output = &clientauthentication.ExecCredential{
|
|
Status: &clientauthentication.ExecCredentialStatus{
|
|
ClientCertificateData: string(cert),
|
|
ClientKeyData: string(key),
|
|
ExpirationTimestamp: &v1.Time{Time: now.Add(time.Hour)},
|
|
},
|
|
}
|
|
get(t, "valid TLS cert", false)
|
|
|
|
// Advance time to force re-exec.
|
|
nCert, nKey := genClientCert(t)
|
|
now = now.Add(time.Hour * 2)
|
|
output = &clientauthentication.ExecCredential{
|
|
Status: &clientauthentication.ExecCredentialStatus{
|
|
ClientCertificateData: string(nCert),
|
|
ClientKeyData: string(nKey),
|
|
ExpirationTimestamp: &v1.Time{Time: now.Add(time.Hour)},
|
|
},
|
|
}
|
|
get(t, "untrusted TLS cert", true)
|
|
|
|
now = now.Add(time.Hour * 2)
|
|
output = &clientauthentication.ExecCredential{
|
|
Status: &clientauthentication.ExecCredentialStatus{
|
|
ClientCertificateData: string(cert),
|
|
ClientKeyData: string(key),
|
|
ExpirationTimestamp: &v1.Time{Time: now.Add(time.Hour)},
|
|
},
|
|
}
|
|
get(t, "valid TLS cert again", false)
|
|
}
|
|
|
|
func TestConcurrentUpdateTransportConfig(t *testing.T) {
|
|
n := time.Now()
|
|
now := func() time.Time { return n }
|
|
|
|
env := []string{""}
|
|
environ := func() []string {
|
|
s := make([]string, len(env))
|
|
copy(s, env)
|
|
return s
|
|
}
|
|
|
|
c := api.ExecConfig{
|
|
Command: "./testdata/test-plugin.sh",
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
}
|
|
a, err := newAuthenticator(newCache(), func(_ int) bool { return false }, &c, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
a.environ = environ
|
|
a.now = now
|
|
a.stderr = io.Discard
|
|
|
|
stopCh := make(chan struct{})
|
|
defer close(stopCh)
|
|
|
|
numConcurrent := 2
|
|
|
|
for i := 0; i < numConcurrent; i++ {
|
|
go func() {
|
|
for {
|
|
tc := &transport.Config{}
|
|
a.UpdateTransportConfig(tc)
|
|
|
|
select {
|
|
case <-stopCh:
|
|
return
|
|
default:
|
|
continue
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
time.Sleep(2 * time.Second)
|
|
}
|
|
|
|
func TestInstallHintRateLimit(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
|
|
threshold int
|
|
interval time.Duration
|
|
|
|
calls int
|
|
perCallAdvance time.Duration
|
|
|
|
wantInstallHint int
|
|
}{
|
|
{
|
|
name: "print-up-to-threshold",
|
|
threshold: 2,
|
|
interval: time.Second,
|
|
calls: 10,
|
|
wantInstallHint: 2,
|
|
},
|
|
{
|
|
name: "after-interval-threshold-resets",
|
|
threshold: 2,
|
|
interval: time.Second * 5,
|
|
calls: 10,
|
|
perCallAdvance: time.Second,
|
|
wantInstallHint: 4,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
c := api.ExecConfig{
|
|
Command: "does not exist",
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
InstallHint: "some install hint",
|
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
|
}
|
|
a, err := newAuthenticator(newCache(), func(_ int) bool { return false }, &c, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a.sometimes.threshold = test.threshold
|
|
a.sometimes.interval = test.interval
|
|
|
|
clock := testingclock.NewFakeClock(time.Now())
|
|
a.sometimes.clock = clock
|
|
|
|
count := 0
|
|
for i := 0; i < test.calls; i++ {
|
|
err := a.refreshCredsLocked()
|
|
if strings.Contains(err.Error(), c.InstallHint) {
|
|
count++
|
|
}
|
|
|
|
clock.SetTime(clock.Now().Add(test.perCallAdvance))
|
|
}
|
|
|
|
if test.wantInstallHint != count {
|
|
t.Errorf(
|
|
"%s: expected install hint %d times got %d",
|
|
test.name,
|
|
test.wantInstallHint,
|
|
count,
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// genClientCert generates an x509 certificate for testing. Certificate and key
|
|
// are returned in PEM encoding. The generated cert expires in 24 hours.
|
|
func genClientCert(t *testing.T) ([]byte, []byte) {
|
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
keyRaw, err := x509.MarshalECPrivateKey(key)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cert := &x509.Certificate{
|
|
SerialNumber: serialNumber,
|
|
Subject: pkix.Name{Organization: []string{"Acme Co"}},
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().Add(24 * time.Hour),
|
|
|
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
BasicConstraintsValid: true,
|
|
}
|
|
certRaw, err := x509.CreateCertificate(rand.Reader, cert, cert, key.Public(), key)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certRaw}),
|
|
pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyRaw})
|
|
}
|