func TestUnprivilegedNewProject(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // confirm that we have access to request the project allowed, err := valerieOpenshiftClient.ProjectRequests().List(labels.Everything(), fields.Everything()) if err != nil { t.Fatalf("unexpected error: %v", err) } if allowed.Status != kapi.StatusSuccess { t.Fatalf("expected %v, got %v", kapi.StatusSuccess, allowed.Status) } requestProject := oc.NewProjectOptions{ ProjectName: "new-project", DisplayName: "display name here", Description: "the special description", Client: valerieOpenshiftClient, Out: ioutil.Discard, } if err := requestProject.Run(); err != nil { t.Fatalf("unexpected error: %v", err) } waitForProject(t, valerieOpenshiftClient, "new-project", 5*time.Second, 10) if err := requestProject.Run(); !kapierrors.IsAlreadyExists(err) { t.Fatalf("expected an already exists error, but got %v", err) } }
func TestBootstrapPolicyAuthenticatedUsersAgainstOpenshiftNamespace(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } openshiftSharedResourcesNamespace := "openshift" if _, err := valerieOpenshiftClient.Templates(openshiftSharedResourcesNamespace).List(labels.Everything(), fields.Everything()); err != nil { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.Templates(kapi.NamespaceDefault).List(labels.Everything(), fields.Everything()); err == nil || !kapierror.IsForbidden(err) { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.ImageStreams(openshiftSharedResourcesNamespace).List(labels.Everything(), fields.Everything()); err != nil { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.ImageStreams(kapi.NamespaceDefault).List(labels.Everything(), fields.Everything()); err == nil || !kapierror.IsForbidden(err) { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.ImageStreamTags(openshiftSharedResourcesNamespace).Get("name", "tag"); !kapierror.IsNotFound(err) { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.ImageStreamTags(kapi.NamespaceDefault).Get("name", "tag"); err == nil || !kapierror.IsForbidden(err) { t.Errorf("unexpected error: %v", err) } }
func TestUnprivilegedNewProjectDenied(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } role, err := clusterAdminClient.ClusterRoles().Get(bootstrappolicy.SelfProvisionerRoleName) if err != nil { t.Fatalf("unexpected error: %v", err) } role.Rules = []authorizationapi.PolicyRule{} if _, err := clusterAdminClient.ClusterRoles().Update(role); err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // confirm that we have access to request the project _, err = valerieOpenshiftClient.ProjectRequests().List(labels.Everything(), fields.Everything()) if err == nil { t.Fatalf("expected error: %v", err) } expectedError := `You may not request a new project via this API.` if (err != nil) && (err.Error() != expectedError) { t.Fatalf("expected\n\t%v\ngot\n\t%v", expectedError, err.Error()) } }
func TestBootstrapPolicySelfSubjectAccessReviews(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // can I get a subjectaccessreview on myself even if I have no rights to do it generally askCanICreatePolicyBindings := &authorizationapi.SubjectAccessReview{Verb: "create", Resource: "policybindings"} subjectAccessReviewTest{ clientInterface: valerieOpenshiftClient.SubjectAccessReviews("openshift"), review: askCanICreatePolicyBindings, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "valerie" cannot create policybindings in project "openshift"`, Namespace: "openshift", }, }.run(t) // I shouldn't be allowed to ask whether someone else can perform an action askCanClusterAdminsCreateProject := &authorizationapi.SubjectAccessReview{Groups: util.NewStringSet("system:cluster-admins"), Verb: "create", Resource: "projects"} subjectAccessReviewTest{ clientInterface: valerieOpenshiftClient.SubjectAccessReviews("openshift"), review: askCanClusterAdminsCreateProject, err: `User "valerie" cannot create subjectaccessreviews in project "openshift"`, }.run(t) }
func TestAuthorizationRestrictedAccessForProjectAdmins(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } haroldClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } markClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark") if err != nil { t.Fatalf("unexpected error: %v", err) } _, err = haroldClient.DeploymentConfigs("hammer-project").List(labels.Everything(), fields.Everything()) if err != nil { t.Fatalf("unexpected error: %v", err) } _, err = markClient.DeploymentConfigs("hammer-project").List(labels.Everything(), fields.Everything()) if (err == nil) || !kapierror.IsForbidden(err) { t.Fatalf("unexpected error: %v", err) } // projects are a special case where a get of a project actually sets a namespace. Make sure that // the namespace is properly special cased and set for authorization rules _, err = haroldClient.Projects().Get("hammer-project") if err != nil { t.Fatalf("unexpected error: %v", err) } _, err = markClient.Projects().Get("hammer-project") if (err == nil) || !kapierror.IsForbidden(err) { t.Fatalf("unexpected error: %v", err) } // wait for the project authorization cache to catch the change. It is on a one second period waitForProject(t, haroldClient, "hammer-project", 1*time.Second, 10) waitForProject(t, markClient, "mallet-project", 1*time.Second, 10) }
func TestOAuthDisabled(t *testing.T) { // Build master config masterOptions, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } // Disable OAuth masterOptions.OAuthConfig = nil // Start server clusterAdminKubeConfig, err := testutil.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } client, err := testutil.GetClusterAdminKubeClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // Make sure cert auth still works namespaces, err := client.Namespaces().List(labels.Everything(), fields.Everything()) if err != nil { t.Fatalf("Unexpected error %v", err) } if len(namespaces.Items) == 0 { t.Errorf("Expected namespaces, got none") } // Use the server and CA info anonConfig := kclient.Config{} anonConfig.Host = clientConfig.Host anonConfig.CAFile = clientConfig.CAFile anonConfig.CAData = clientConfig.CAData // Make sure we can't authenticate using OAuth if _, err := tokencmd.RequestToken(&anonConfig, nil, "username", "password"); err == nil { t.Error("Expected error, got none") } }
func TestAuthorizationResourceAccessReview(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } haroldClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } markClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark") if err != nil { t.Fatalf("unexpected error: %v", err) } addValerie := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.ViewRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("hammer-project", haroldClient), Users: []string{"valerie"}, } if err := addValerie.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } addEdgar := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.EditRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("mallet-project", markClient), Users: []string{"edgar"}, } if err := addEdgar.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } requestWhoCanViewDeployments := &authorizationapi.ResourceAccessReview{Verb: "get", Resource: "deployments"} { test := resourceAccessReviewTest{ clientInterface: haroldClient.ResourceAccessReviews("hammer-project"), review: requestWhoCanViewDeployments, response: authorizationapi.ResourceAccessReviewResponse{ Users: util.NewStringSet("harold", "valerie"), Groups: globalClusterAdminGroups, Namespace: "hammer-project", }, } test.response.Users.Insert(globalClusterAdminUsers.List()...) test.response.Groups.Insert("system:cluster-readers") test.run(t) } { test := resourceAccessReviewTest{ clientInterface: markClient.ResourceAccessReviews("mallet-project"), review: requestWhoCanViewDeployments, response: authorizationapi.ResourceAccessReviewResponse{ Users: util.NewStringSet("mark", "edgar"), Groups: globalClusterAdminGroups, Namespace: "mallet-project", }, } test.response.Users.Insert(globalClusterAdminUsers.List()...) test.response.Groups.Insert("system:cluster-readers") test.run(t) } // mark should not be able to make global access review requests { test := resourceAccessReviewTest{ clientInterface: markClient.ClusterResourceAccessReviews(), review: requestWhoCanViewDeployments, err: "cannot ", } test.run(t) } // a cluster-admin should be able to make global access review requests { test := resourceAccessReviewTest{ clientInterface: clusterAdminClient.ClusterResourceAccessReviews(), review: requestWhoCanViewDeployments, response: authorizationapi.ResourceAccessReviewResponse{ Users: globalClusterAdminUsers, Groups: globalClusterAdminGroups, }, } test.response.Groups.Insert("system:cluster-readers") test.run(t) } }
func TestServiceAccountAuthorization(t *testing.T) { saNamespace := api.NamespaceDefault saName := serviceaccountadmission.DefaultServiceAccountName saUsername := serviceaccount.MakeUsername(saNamespace, saName) // Start one OpenShift master as "cluster1" to play the external kube server cluster1MasterConfig, cluster1AdminConfigFile, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } cluster1AdminConfig, err := testutil.GetClusterAdminClientConfig(cluster1AdminConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } cluster1AdminKubeClient, err := testutil.GetClusterAdminKubeClient(cluster1AdminConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } cluster1AdminOSClient, err := testutil.GetClusterAdminClient(cluster1AdminConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } // Get a service account token and build a client saToken, err := waitForServiceAccountToken(cluster1AdminKubeClient, saNamespace, saName, 20, time.Second) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(saToken) == 0 { t.Fatalf("token was not created") } cluster1SAClientConfig := kclient.Config{ Host: cluster1AdminConfig.Host, Prefix: cluster1AdminConfig.Prefix, BearerToken: saToken, TLSClientConfig: kclient.TLSClientConfig{ CAFile: cluster1AdminConfig.CAFile, CAData: cluster1AdminConfig.CAData, }, } cluster1SAKubeClient, err := kclient.New(&cluster1SAClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // Make sure the service account doesn't have access failNS := &api.Namespace{ObjectMeta: api.ObjectMeta{Name: "test-fail"}} if _, err := cluster1SAKubeClient.Namespaces().Create(failNS); !errors.IsForbidden(err) { t.Fatalf("expected forbidden error, got %v", err) } // Make the service account a cluster admin on cluster1 addRoleOptions := &policy.RoleModificationOptions{ RoleName: bootstrappolicy.ClusterAdminRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(cluster1AdminOSClient), Users: []string{saUsername}, } if err := addRoleOptions.AddRole(); err != nil { t.Fatalf("could not add role to service account") } // Give the policy cache a second to catch it's breath time.Sleep(time.Second) // Make sure the service account now has access // This tests authentication using the etcd-based token getter passNS := &api.Namespace{ObjectMeta: api.ObjectMeta{Name: "test-pass"}} if _, err := cluster1SAKubeClient.Namespaces().Create(passNS); err != nil { t.Fatalf("unexpected error: %v", err) } // Create a kubeconfig from the serviceaccount config cluster1SAKubeConfigFile, err := ioutil.TempFile(testutil.GetBaseDir(), "cluster1-service-account.kubeconfig") if err != nil { t.Fatalf("error creating tmpfile: %v", err) } defer os.Remove(cluster1SAKubeConfigFile.Name()) if err := writeClientConfigToKubeConfig(cluster1SAClientConfig, cluster1SAKubeConfigFile.Name()); err != nil { t.Fatalf("error creating kubeconfig: %v", err) } // Set up cluster 2 to run against cluster 1 as external kubernetes cluster2MasterConfig, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } // Don't start kubernetes in process cluster2MasterConfig.KubernetesMasterConfig = nil // Connect to cluster1 using the service account credentials cluster2MasterConfig.MasterClients.ExternalKubernetesKubeConfig = cluster1SAKubeConfigFile.Name() // Don't start etcd cluster2MasterConfig.EtcdConfig = nil // Use the same credentials as cluster1 to connect to existing etcd cluster2MasterConfig.EtcdClientInfo = cluster1MasterConfig.EtcdClientInfo // Set a custom etcd prefix to make sure data is getting sent to cluster1 cluster2MasterConfig.EtcdStorageConfig.KubernetesStoragePrefix += "2" cluster2MasterConfig.EtcdStorageConfig.OpenShiftStoragePrefix += "2" // Don't manage any names in cluster2 cluster2MasterConfig.ServiceAccountConfig.ManagedNames = []string{} // Don't create any service account tokens in cluster2 cluster2MasterConfig.ServiceAccountConfig.PrivateKeyFile = "" // Use the same public keys to validate tokens as cluster1 cluster2MasterConfig.ServiceAccountConfig.PublicKeyFiles = cluster1MasterConfig.ServiceAccountConfig.PublicKeyFiles // Start cluster 2 (without clearing etcd) and get admin client configs and clients cluster2Options := testutil.TestOptions{DeleteAllEtcdKeys: false} cluster2AdminConfigFile, err := testutil.StartConfiguredMasterWithOptions(cluster2MasterConfig, cluster2Options) if err != nil { t.Fatalf("unexpected error: %v", err) } cluster2AdminConfig, err := testutil.GetClusterAdminClientConfig(cluster2AdminConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } cluster2AdminOSClient, err := testutil.GetClusterAdminClient(cluster2AdminConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } // Build a client to use the same service account token against cluster2 cluster2SAClientConfig := cluster1SAClientConfig cluster2SAClientConfig.Host = cluster2AdminConfig.Host cluster2SAKubeClient, err := kclient.New(&cluster2SAClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // Make sure the service account doesn't have access // A forbidden error makes sure the token was recognized, and policy denied us // This exercises the client-based token getter // It also makes sure we don't loop back through the cluster2 kube proxy which would cause an auth loop failNS2 := &api.Namespace{ObjectMeta: api.ObjectMeta{Name: "test-fail2"}} if _, err := cluster2SAKubeClient.Namespaces().Create(failNS2); !errors.IsForbidden(err) { t.Fatalf("expected forbidden error, got %v", err) } // Make the service account a cluster admin on cluster2 addRoleOptions2 := &policy.RoleModificationOptions{ RoleName: bootstrappolicy.ClusterAdminRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(cluster2AdminOSClient), Users: []string{saUsername}, } if err := addRoleOptions2.AddRole(); err != nil { t.Fatalf("could not add role to service account") } // Give the policy cache a second to catch it's breath time.Sleep(time.Second) // Make sure the service account now has access to cluster2 passNS2 := &api.Namespace{ObjectMeta: api.ObjectMeta{Name: "test-pass2"}} if _, err := cluster2SAKubeClient.Namespaces().Create(passNS2); err != nil { t.Fatalf("unexpected error: %v", err) } // Make sure the ns actually got created in cluster1 if _, err := cluster1SAKubeClient.Namespaces().Get(passNS2.Name); err != nil { t.Fatalf("unexpected error: %v", err) } }
func TestBasicGroupManipulation(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // make sure we don't get back system groups firstValerie, err := clusterAdminClient.Users().Get("valerie") if err != nil { t.Fatalf("unexpected error: %v", err) } if len(firstValerie.Groups) != 0 { t.Errorf("unexpected groups: %v", firstValerie.Groups) } // make sure that user/~ returns groups for unbacked users expectedClusterAdminGroups := []string{"system:cluster-admins"} clusterAdminUser, err := clusterAdminClient.Users().Get("~") if err != nil { t.Fatalf("unexpected error: %v", err) } if !reflect.DeepEqual(clusterAdminUser.Groups, expectedClusterAdminGroups) { t.Errorf("expected %v, got %v", clusterAdminUser.Groups, expectedClusterAdminGroups) } valerieGroups := []string{"thegroup"} firstValerie.Groups = append(firstValerie.Groups, valerieGroups...) _, err = clusterAdminClient.Users().Update(firstValerie) if err != nil { t.Errorf("unexpected error: %v", err) } // make sure that user/~ doesn't get back system groups when it merges secondValerie, err := valerieOpenshiftClient.Users().Get("~") if err != nil { t.Fatalf("unexpected error: %v", err) } if !reflect.DeepEqual(secondValerie.Groups, valerieGroups) { t.Errorf("expected %v, got %v", secondValerie.Groups, valerieGroups) } _, err = valerieOpenshiftClient.Projects().Get("empty") if err == nil { t.Fatalf("expected error") } emptyProject := &projectapi.Project{} emptyProject.Name = "empty" _, err = clusterAdminClient.Projects().Create(emptyProject) if err != nil { t.Fatalf("unexpected error: %v", err) } roleBinding := &authorizationapi.RoleBinding{} roleBinding.Name = "admins" roleBinding.RoleRef.Name = "admin" roleBinding.Groups = util.NewStringSet(valerieGroups...) _, err = clusterAdminClient.RoleBindings("empty").Create(roleBinding) if err != nil { t.Fatalf("unexpected error: %v", err) } // make sure that user groups are respected for policy _, err = valerieOpenshiftClient.Projects().Get("empty") if err != nil { t.Fatalf("unexpected error: %v", err) } }
func TestUnprivilegedNewProjectFromTemplate(t *testing.T) { namespace := "foo" templateName := "bar" masterOptions, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.ProjectConfig.ProjectRequestTemplate = namespace + "/" + templateName clusterAdminKubeConfig, err := testutil.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } if _, err := clusterAdminClient.Projects().Create(&projectapi.Project{ObjectMeta: kapi.ObjectMeta{Name: namespace}}); err != nil { t.Fatalf("unexpected error: %v", err) } template := projectrequeststorage.DefaultTemplate() template.Name = templateName template.Namespace = namespace template.Objects[0].(*projectapi.Project).Annotations["extra"] = "here" _, err = clusterAdminClient.Templates(namespace).Create(template) if err != nil { t.Fatalf("unexpected error: %v", err) } requestProject := oc.NewProjectOptions{ ProjectName: "new-project", DisplayName: "display name here", Description: "the special description", Client: valerieOpenshiftClient, Out: ioutil.Discard, } if err := requestProject.Run(); err != nil { t.Fatalf("unexpected error: %v", err) } waitForProject(t, valerieOpenshiftClient, "new-project", 5*time.Second, 10) project, err := valerieOpenshiftClient.Projects().Get("new-project") if err != nil { t.Fatalf("unexpected error: %v", err) } if project.Annotations["extra"] != "here" { t.Errorf("unexpected project %#v", project) } if err := clusterAdminClient.Templates(namespace).Delete(templateName); err != nil { t.Fatalf("unexpected error: %v", err) } requestProject.ProjectName = "different" // This should fail during the template retrieve if err := requestProject.Run(); !kapierrors.IsNotFound(err) { t.Fatalf("expected a not found error, but got %v", err) } }
func TestLogin(t *testing.T) { clientcmd.DefaultCluster = clientcmdapi.Cluster{Server: ""} _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } username := "******" password := "******" project := "the-singularity-is-near" server := clusterAdminClientConfig.Host loginOptions := newLoginOptions(server, username, password, true) if err := loginOptions.GatherInfo(); err != nil { t.Fatalf("Error trying to determine server info: %v", err) } if loginOptions.Username != username { t.Fatalf("Unexpected user after authentication: %#v", loginOptions) } newProjectOptions := &newproject.NewProjectOptions{ Client: clusterAdminClient, ProjectName: project, AdminRole: bootstrappolicy.AdminRoleName, AdminUser: username, } if err := newProjectOptions.Run(false); err != nil { t.Fatalf("unexpected error, a project is required to continue: %v", err) } oClient, _ := client.New(loginOptions.Config) p, err := oClient.Projects().Get(project) if err != nil { t.Errorf("unexpected error: %v", err) } if p.Name != project { t.Fatalf("unexpected project: %#v", p) } // TODO Commented because of incorrectly hitting cache when listing projects. // Should be enabled again when cache eviction is properly fixed. // err = loginOptions.GatherProjectInfo() // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // if loginOptions.Project != project { // t.Fatalf("Expected project %v but got %v", project, loginOptions.Project) // } // configFile, err := ioutil.TempFile("", "openshiftconfig") // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // defer os.Remove(configFile.Name()) // if _, err = loginOptions.SaveConfig(configFile.Name()); err != nil { // t.Fatalf("unexpected error: %v", err) // } userWhoamiOptions := cmd.WhoAmIOptions{oClient.Users(), ioutil.Discard} retrievedUser, err := userWhoamiOptions.WhoAmI() if err != nil { t.Errorf("unexpected error: %v", err) } if retrievedUser.Name != username { t.Errorf("expected %v, got %v", retrievedUser.Name, username) } adminWhoamiOptions := cmd.WhoAmIOptions{clusterAdminClient.Users(), ioutil.Discard} retrievedAdmin, err := adminWhoamiOptions.WhoAmI() if err != nil { t.Errorf("unexpected error: %v", err) } if retrievedAdmin.Name != "system:admin" { t.Errorf("expected %v, got %v", retrievedAdmin.Name, "system:admin") } }
func TestHTPasswd(t *testing.T) { htpasswdFile, err := ioutil.TempFile("", "test.htpasswd") if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.Remove(htpasswdFile.Name()) masterOptions, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.OAuthConfig.IdentityProviders[0] = configapi.IdentityProvider{ Name: "htpasswd", UseAsChallenger: true, UseAsLogin: true, Provider: runtime.EmbeddedObject{ &configapi.HTPasswdPasswordIdentityProvider{ File: htpasswdFile.Name(), }, }, } clusterAdminKubeConfig, err := testutil.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } // Use the server and CA info anonConfig := kclient.Config{} anonConfig.Host = clientConfig.Host anonConfig.CAFile = clientConfig.CAFile anonConfig.CAData = clientConfig.CAData // Make sure we can't authenticate if _, err := tokencmd.RequestToken(&anonConfig, nil, "username", "password"); err == nil { t.Error("Expected error, got none") } // Update the htpasswd file with output of `htpasswd -n -b username password` userpass := "******" ioutil.WriteFile(htpasswdFile.Name(), []byte(userpass), os.FileMode(0600)) // Make sure we can get a token accessToken, err := tokencmd.RequestToken(&anonConfig, nil, "username", "password") if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(accessToken) == 0 { t.Errorf("Expected access token, got none") } // Make sure we can use the token, and it represents who we expect userConfig := anonConfig userConfig.BearerToken = accessToken userClient, err := client.New(&userConfig) if err != nil { t.Fatalf("Unexpected error: %v", err) } user, err := userClient.Users().Get("~") if err != nil { t.Fatalf("Unexpected error: %v", err) } if user.Name != "username" { t.Fatalf("Expected username as the user, got %v", user) } }
func TestAuthorizationSubjectAccessReview(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } haroldClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } markClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark") if err != nil { t.Fatalf("unexpected error: %v", err) } dannyClient, err := testutil.GetClientForUser(*clusterAdminClientConfig, "danny") if err != nil { t.Fatalf("error requesting token: %v", err) } addDanny := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.ViewRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("default", clusterAdminClient), Users: []string{"danny"}, } if err := addDanny.AddRole(); err != nil { t.Errorf("unexpected error: %v", err) } askCanDannyGetProject := &authorizationapi.SubjectAccessReview{User: "******", Verb: "get", Resource: "projects"} subjectAccessReviewTest{ description: "cluster admin told danny can get project default", clientInterface: clusterAdminClient.SubjectAccessReviews("default"), review: askCanDannyGetProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in default", Namespace: "default", }, }.run(t) subjectAccessReviewTest{ description: "cluster admin told danny cannot get projects cluster-wide", clientInterface: clusterAdminClient.ClusterSubjectAccessReviews(), review: askCanDannyGetProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "danny" cannot get projects at the cluster scope`, Namespace: "", }, }.run(t) subjectAccessReviewTest{ description: "as danny, can I make cluster subject access reviews", clientInterface: dannyClient.ClusterSubjectAccessReviews(), review: askCanDannyGetProject, err: `User "danny" cannot create subjectaccessreviews at the cluster scope`, }.run(t) addValerie := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.ViewRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("hammer-project", haroldClient), Users: []string{"valerie"}, } if err := addValerie.AddRole(); err != nil { t.Errorf("unexpected error: %v", err) } addEdgar := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.EditRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("mallet-project", markClient), Users: []string{"edgar"}, } if err := addEdgar.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } askCanValerieGetProject := &authorizationapi.SubjectAccessReview{User: "******", Verb: "get", Resource: "projects"} subjectAccessReviewTest{ description: "harold told valerie can get project hammer-project", clientInterface: haroldClient.SubjectAccessReviews("hammer-project"), review: askCanValerieGetProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in hammer-project", Namespace: "hammer-project", }, }.run(t) subjectAccessReviewTest{ description: "mark told valerie cannot get project mallet-project", clientInterface: markClient.SubjectAccessReviews("mallet-project"), review: askCanValerieGetProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "valerie" cannot get projects in project "mallet-project"`, Namespace: "mallet-project", }, }.run(t) askCanEdgarDeletePods := &authorizationapi.SubjectAccessReview{User: "******", Verb: "delete", Resource: "pods"} subjectAccessReviewTest{ description: "mark told edgar can delete pods in mallet-project", clientInterface: markClient.SubjectAccessReviews("mallet-project"), review: askCanEdgarDeletePods, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in mallet-project", Namespace: "mallet-project", }, }.run(t) subjectAccessReviewTest{ description: "harold denied ability to run subject access review in project mallet-project", clientInterface: haroldClient.SubjectAccessReviews("mallet-project"), review: askCanEdgarDeletePods, err: `User "harold" cannot create subjectaccessreviews in project "mallet-project"`, }.run(t) askCanHaroldUpdateProject := &authorizationapi.SubjectAccessReview{User: "******", Verb: "update", Resource: "projects"} subjectAccessReviewTest{ description: "harold told harold can update project hammer-project", clientInterface: haroldClient.SubjectAccessReviews("hammer-project"), review: askCanHaroldUpdateProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in hammer-project", Namespace: "hammer-project", }, }.run(t) askCanClusterAdminsCreateProject := &authorizationapi.SubjectAccessReview{Groups: util.NewStringSet("system:cluster-admins"), Verb: "create", Resource: "projects"} subjectAccessReviewTest{ description: "cluster admin told cluster admins can create projects", clientInterface: clusterAdminClient.ClusterSubjectAccessReviews(), review: askCanClusterAdminsCreateProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by cluster rule:", Namespace: "", }, }.run(t) subjectAccessReviewTest{ description: "harold denied ability to run cluster subject access review", clientInterface: haroldClient.ClusterSubjectAccessReviews(), review: askCanClusterAdminsCreateProject, err: `User "harold" cannot create subjectaccessreviews at the cluster scope`, }.run(t) askCanICreatePods := &authorizationapi.SubjectAccessReview{Verb: "create", Resource: "pods"} subjectAccessReviewTest{ description: "harold told he can create pods in project hammer-project", clientInterface: haroldClient.SubjectAccessReviews("hammer-project"), review: askCanICreatePods, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in hammer-project", Namespace: "hammer-project", }, }.run(t) askCanICreatePolicyBindings := &authorizationapi.SubjectAccessReview{Verb: "create", Resource: "policybindings"} subjectAccessReviewTest{ description: "harold told he can create policybindings in project hammer-project", clientInterface: haroldClient.SubjectAccessReviews("hammer-project"), review: askCanICreatePolicyBindings, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "harold" cannot create policybindings in project "hammer-project"`, Namespace: "hammer-project", }, }.run(t) }
func TestOAuthBasicAuthPassword(t *testing.T) { remotePrefix := "remote" expectedLogin := "******" expectedPassword := "******" expectedAuthHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(expectedLogin+":"+expectedPassword)) expectedUsername := remotePrefix + expectedLogin // Create tempfiles with certs and keys we're going to use certNames := map[string]string{} for certName, certContents := range basicAuthCerts { f, err := ioutil.TempFile("", certName) if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.Remove(f.Name()) if err := ioutil.WriteFile(f.Name(), certContents, os.FileMode(0600)); err != nil { t.Fatalf("unexpected error: %v", err) } certNames[certName] = f.Name() } // Build client cert pool clientCAs, err := util.CertPoolFromFile(certNames[basicAuthRemoteCACert]) if err != nil { t.Fatalf("unexpected error: %v", err) } // Build remote handler remoteHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { if req.TLS == nil { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Expected TLS") } if len(req.TLS.VerifiedChains) != 1 { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Expected peer cert verified by server") } if req.Header.Get("Authorization") != expectedAuthHeader { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Unexpected auth header: %s", req.Header.Get("Authorization")) } w.Header().Set("Content-Type", "application/json") w.Write([]byte(fmt.Sprintf(`{"sub":"%s"}`, expectedUsername))) }) // Start remote server remoteAddr, err := testutil.FindAvailableBindAddress(9443, 9999) if err != nil { t.Fatalf("Couldn't get free address for test server: %v", err) } remoteServer := &http.Server{ Addr: remoteAddr, Handler: remoteHandler, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, TLSConfig: &tls.Config{ // Change default from SSLv3 to TLSv1.0 (because of POODLE vulnerability) MinVersion: tls.VersionTLS10, // RequireAndVerifyClientCert lets us limit requests to ones with a valid client certificate ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: clientCAs, }, } go func() { if err := remoteServer.ListenAndServeTLS(certNames[basicAuthRemoteServerCert], certNames[basicAuthRemoteServerKey]); err != nil { t.Fatalf("unexpected error: %v", err) } }() // Build master config masterOptions, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.OAuthConfig.IdentityProviders[0] = configapi.IdentityProvider{ Name: "basicauth", UseAsChallenger: true, UseAsLogin: true, Provider: runtime.EmbeddedObject{ &configapi.BasicAuthPasswordIdentityProvider{ RemoteConnectionInfo: configapi.RemoteConnectionInfo{ URL: fmt.Sprintf("https://%s", remoteAddr), CA: certNames[basicAuthRemoteCACert], ClientCert: configapi.CertInfo{ CertFile: certNames[basicAuthClientCert], KeyFile: certNames[basicAuthClientKey], }, }, }, }, } // Start server clusterAdminKubeConfig, err := testutil.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // Use the server and CA info anonConfig := kclient.Config{} anonConfig.Host = clientConfig.Host anonConfig.CAFile = clientConfig.CAFile anonConfig.CAData = clientConfig.CAData // Make sure we can get a token accessToken, err := tokencmd.RequestToken(&anonConfig, nil, expectedLogin, expectedPassword) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(accessToken) == 0 { t.Errorf("Expected access token, got none") } // Make sure we can use the token, and it represents who we expect userConfig := anonConfig userConfig.BearerToken = accessToken userClient, err := client.New(&userConfig) if err != nil { t.Fatalf("Unexpected error: %v", err) } user, err := userClient.Users().Get("~") if err != nil { t.Fatalf("Unexpected error: %v", err) } if user.Name != expectedUsername { t.Fatalf("Expected username as the user, got %v", user) } }
func TestOAuthRequestHeader(t *testing.T) { // Write cert we're going to use to verify OAuth requestheader requests caFile, err := ioutil.TempFile("", "test.crt") if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.Remove(caFile.Name()) if err := ioutil.WriteFile(caFile.Name(), rootCACert, os.FileMode(0600)); err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.OAuthConfig.IdentityProviders[0] = configapi.IdentityProvider{ Name: "requestheader", UseAsChallenger: false, UseAsLogin: false, Provider: runtime.EmbeddedObject{ &configapi.RequestHeaderIdentityProvider{ ClientCA: caFile.Name(), Headers: []string{"My-Remote-User", "SSO-User"}, }, }, } // Start server clusterAdminKubeConfig, err := testutil.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // Use the server and CA info, but no client cert info anonConfig := kclient.Config{} anonConfig.Host = clientConfig.Host anonConfig.CAFile = clientConfig.CAFile anonConfig.CAData = clientConfig.CAData // Build the authorize request with the My-Remote-User header authorizeURL := clientConfig.Host + "/oauth/authorize?client_id=openshift-challenging-client&response_type=token" req, err := http.NewRequest("GET", authorizeURL, nil) req.Header.Set("My-Remote-User", "myuser") // Make the request without cert auth transport, err := kclient.TransportFor(&anonConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } resp, err := transport.RoundTrip(req) if err != nil { t.Fatalf("unexpected error: %v", err) } redirect, err := resp.Location() if err != nil { t.Fatalf("expected 302 redirect, got error: %v", err) } if redirect.Query().Get("error") == "" { t.Fatalf("expected unsuccessful token request, got redirected to %v", redirect.String()) } // Use the server and CA info, with cert info authProxyConfig := anonConfig authProxyConfig.CertData = clientCert authProxyConfig.KeyData = clientKey // Make the request with cert info transport, err = kclient.TransportFor(&authProxyConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } resp, err = transport.RoundTrip(req) if err != nil { t.Fatalf("unexpected error: %v", err) } redirect, err = resp.Location() if err != nil { t.Fatalf("expected 302 redirect, got error: %v", err) } if redirect.Query().Get("error") != "" { t.Fatalf("expected successful token request, got error %v", redirect.String()) } // Extract the access_token // group #0 is everything. #1 #2 #3 accessTokenRedirectRegex := regexp.MustCompile(`(^|&)access_token=([^&]+)($|&)`) accessToken := "" if matches := accessTokenRedirectRegex.FindStringSubmatch(redirect.Fragment); matches != nil { accessToken = matches[2] } if accessToken == "" { t.Fatalf("Expected access token, got %s", redirect.String()) } // Make sure we can use the token, and it represents who we expect userConfig := anonConfig userConfig.BearerToken = accessToken userClient, err := client.New(&userConfig) if err != nil { t.Fatalf("Unexpected error: %v", err) } user, err := userClient.Users().Get("~") if err != nil { t.Fatalf("Unexpected error: %v", err) } if user.Name != "myuser" { t.Fatalf("Expected myuser as the user, got %v", user) } }
func TestWebhookGitHubPushWithImage(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } err = testutil.CreateNamespace(clusterAdminKubeConfig, testutil.Namespace()) if err != nil { t.Errorf("unexpected error: %v", err) } clusterAdminKubeClient, err := testutil.GetClusterAdminKubeClient(clusterAdminKubeConfig) checkErr(t, err) if err := testutil.WaitForServiceAccounts(clusterAdminKubeClient, testutil.Namespace(), []string{bootstrappolicy.BuilderServiceAccountName, bootstrappolicy.DefaultServiceAccountName}); err != nil { t.Errorf("unexpected error: %v", err) } // create imagerepo imageStream := &imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{Name: "image-stream"}, Spec: imageapi.ImageStreamSpec{ DockerImageRepository: "registry:3000/integration/imageStream", Tags: map[string]imageapi.TagReference{ "validTag": { From: &kapi.ObjectReference{ Kind: "DockerImage", Name: "registry:3000/integration/imageStream:success", }, }, }, }, } if _, err := clusterAdminClient.ImageStreams(testutil.Namespace()).Create(imageStream); err != nil { t.Fatalf("Unexpected error: %v", err) } ism := &imageapi.ImageStreamMapping{ ObjectMeta: kapi.ObjectMeta{Name: "image-stream"}, Tag: "validTag", Image: imageapi.Image{ ObjectMeta: kapi.ObjectMeta{ Name: "myimage", }, DockerImageReference: "registry:3000/integration/imageStream:success", }, } if err := clusterAdminClient.ImageStreamMappings(testutil.Namespace()).Create(ism); err != nil { t.Fatalf("Unexpected error: %v", err) } // create buildconfig buildConfig := mockBuildConfigImageParms("originalImage", "imageStream", "validTag") if _, err := clusterAdminClient.BuildConfigs(testutil.Namespace()).Create(buildConfig); err != nil { t.Fatalf("Unexpected error: %v", err) } watch, err := clusterAdminClient.Builds(testutil.Namespace()).Watch(labels.Everything(), fields.Everything(), "0") if err != nil { t.Fatalf("Couldn't subscribe to builds: %v", err) } defer watch.Stop() for _, s := range []string{ "/oapi/v1/namespaces/" + testutil.Namespace() + "/buildconfigs/pushbuild/webhooks/secret101/github", } { // trigger build event sending push notification postFile(clusterAdminClient.RESTClient.Client, "push", "pushevent.json", clusterAdminClientConfig.Host+s, http.StatusOK, t) event := <-watch.ResultChan() actual := event.Object.(*buildapi.Build) // FIXME: I think the build creation is fast and in some situlation we miss // the BuildStatusNew here. Note that this is not a bug, in future we should // move this to use go routine to capture all events. if actual.Status != buildapi.BuildStatusNew && actual.Status != buildapi.BuildStatusPending { t.Errorf("Expected %s or %s, got %s", buildapi.BuildStatusNew, buildapi.BuildStatusPending, actual.Status) } if actual.Parameters.Strategy.DockerStrategy.From.Name != "originalImage" { t.Errorf("Expected %s, got %s", "originalImage", actual.Parameters.Strategy.DockerStrategy.From.Name) } } }
func TestTemplate(t *testing.T) { _, path, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } for _, version := range []string{"v1", "v1beta3"} { config, err := testutil.GetClusterAdminClientConfig(path) if err != nil { t.Fatalf("unexpected error: %v", err) } config.Version = version config.Prefix = "" c, err := client.New(config) if err != nil { t.Fatalf("unexpected error: %v", err) } template := &templateapi.Template{ Parameters: []templateapi.Parameter{ { Name: "NAME", Value: "test", }, }, Objects: []runtime.Object{ &v1beta3.Service{ ObjectMeta: v1beta3.ObjectMeta{ Name: "${NAME}-tester", Namespace: "somevalue", }, Spec: v1beta3.ServiceSpec{ PortalIP: "1.2.3.4", SessionAffinity: "some-bad-${VALUE}", }, }, }, } obj, err := c.TemplateConfigs("default").Create(template) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(obj.Objects) != 1 { t.Fatalf("unexpected object: %#v", obj) } if err := runtime.DecodeList(obj.Objects, runtime.UnstructuredJSONScheme); err != nil { t.Fatalf("unexpected error: %v", err) } svc := obj.Objects[0].(*runtime.Unstructured).Object spec := svc["spec"].(map[string]interface{}) meta := svc["metadata"].(map[string]interface{}) // keep existing values if spec["portalIP"] != "1.2.3.4" { t.Fatalf("unexpected object: %#v", svc) } // replace a value if meta["name"] != "test-tester" { t.Fatalf("unexpected object: %#v", svc) } // clear namespace if meta["namespace"] != "" { t.Fatalf("unexpected object: %#v", svc) } // preserve values exactly if spec["sessionAffinity"] != "some-bad-${VALUE}" { t.Fatalf("unexpected object: %#v", svc) } } }
func TestV2RegistryGetTags(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("error starting master: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("error getting cluster admin client: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("error getting cluster admin client config: %v", err) } user := "******" adminClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, testutil.Namespace(), user) if err != nil { t.Fatalf("error creating project: %v", err) } token, err := tokencmd.RequestToken(clusterAdminClientConfig, nil, user, "password") if err != nil { t.Fatalf("error requesting token: %v", err) } config := `version: 0.1 loglevel: debug http: addr: 127.0.0.1:5000 storage: inmemory: {} auth: openshift: middleware: repository: - name: openshift ` os.Setenv("OPENSHIFT_CA_DATA", string(clusterAdminClientConfig.CAData)) os.Setenv("OPENSHIFT_CERT_DATA", string(clusterAdminClientConfig.CertData)) os.Setenv("OPENSHIFT_KEY_DATA", string(clusterAdminClientConfig.KeyData)) os.Setenv("OPENSHIFT_MASTER", clusterAdminClientConfig.Host) os.Setenv("REGISTRY_URL", "127.0.0.1:5000") go dockerregistry.Execute(strings.NewReader(config)) stream := imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Namespace: testutil.Namespace(), Name: "test", }, } if _, err := adminClient.ImageStreams(testutil.Namespace()).Create(&stream); err != nil { t.Fatalf("error creating image stream: %s", err) } tags, err := getTags(stream.Name, user, token) if err != nil { t.Fatal(err) } if len(tags) > 0 { t.Fatalf("expected 0 tags, got: %#v", tags) } dgst, err := putManifest(stream.Name, user, token) if err != nil { t.Fatal(err) } tags, err = getTags(stream.Name, user, token) if err != nil { t.Fatal(err) } if len(tags) != 1 { t.Fatalf("expected 1 tag, got %d: %v", len(tags), tags) } if tags[0] != imageapi.DefaultImageTag { t.Fatalf("expected latest, got %q", tags[0]) } // test get by tag url := fmt.Sprintf("http://127.0.0.1:5000/v2/%s/%s/manifests/%s", testutil.Namespace(), stream.Name, imageapi.DefaultImageTag) req, err := http.NewRequest("GET", url, nil) if err != nil { t.Fatalf("error creating request: %v", err) } req.SetBasicAuth(user, token) resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatalf("error retrieving manifest from registry: %s", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatalf("unexpected status code: %d", resp.StatusCode) } body, err := ioutil.ReadAll(resp.Body) var retrievedManifest manifest.Manifest if err := json.Unmarshal(body, &retrievedManifest); err != nil { t.Fatalf("error unmarshaling retrieved manifest") } if retrievedManifest.Name != fmt.Sprintf("%s/%s", testutil.Namespace(), stream.Name) { t.Fatalf("unexpected manifest name: %s", retrievedManifest.Name) } if retrievedManifest.Tag != imageapi.DefaultImageTag { t.Fatalf("unexpected manifest tag: %s", retrievedManifest.Tag) } // test get by digest url = fmt.Sprintf("http://127.0.0.1:5000/v2/%s/%s/manifests/%s", testutil.Namespace(), stream.Name, dgst.String()) req, err = http.NewRequest("GET", url, nil) if err != nil { t.Fatalf("error creating request: %v", err) } req.SetBasicAuth(user, token) resp, err = http.DefaultClient.Do(req) if err != nil { t.Fatalf("error retrieving manifest from registry: %s", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatalf("unexpected status code: %d", resp.StatusCode) } body, err = ioutil.ReadAll(resp.Body) if err := json.Unmarshal(body, &retrievedManifest); err != nil { t.Fatalf("error unmarshaling retrieved manifest") } if retrievedManifest.Name != fmt.Sprintf("%s/%s", testutil.Namespace(), stream.Name) { t.Fatalf("unexpected manifest name: %s", retrievedManifest.Name) } if retrievedManifest.Tag != imageapi.DefaultImageTag { t.Fatalf("unexpected manifest tag: %s", retrievedManifest.Tag) } image, err := adminClient.ImageStreamImages(testutil.Namespace()).Get(stream.Name, dgst.String()) if err != nil { t.Fatalf("error getting imageStreamImage: %s", err) } if e, a := fmt.Sprintf("test@%s", dgst.Hex()[:7]), image.Name; e != a { t.Errorf("image name: expected %q, got %q", e, a) } if e, a := dgst.String(), image.Image.Name; e != a { t.Errorf("image name: expected %q, got %q", e, a) } if e, a := fmt.Sprintf("127.0.0.1:5000/%s/%s@%s", testutil.Namespace(), stream.Name, dgst.String()), image.Image.DockerImageReference; e != a { t.Errorf("image dockerImageReference: expected %q, got %q", e, a) } if e, a := "foo", image.Image.DockerImageMetadata.ID; e != a { t.Errorf("image dockerImageMetadata.ID: expected %q, got %q", e, a) } // test auto provisioning otherStream, err := adminClient.ImageStreams(testutil.Namespace()).Get("otherrepo") t.Logf("otherStream=%#v, err=%v", otherStream, err) if err == nil { t.Fatalf("expected error getting otherrepo") } otherDigest, err := putManifest("otherrepo", user, token) if err != nil { t.Fatal(err) } otherStream, err = adminClient.ImageStreams(testutil.Namespace()).Get("otherrepo") if err != nil { t.Fatalf("unexpected error getting otherrepo: %s", err) } if otherStream == nil { t.Fatalf("unexpected nil otherrepo") } if len(otherStream.Status.Tags) != 1 { t.Errorf("expected 1 tag, got %#v", otherStream.Status.Tags) } history, ok := otherStream.Status.Tags[imageapi.DefaultImageTag] if !ok { t.Fatal("unable to find 'latest' tag") } if len(history.Items) != 1 { t.Errorf("expected 1 tag event, got %#v", history.Items) } if e, a := otherDigest.String(), history.Items[0].Image; e != a { t.Errorf("digest: expected %q, got %q", e, a) } }
func TestTriggers_imageChange(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("error starting master: %v", err) } openshiftClusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("error getting OpenShift cluster admin client: %v", err) } openshiftClusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("error getting cluster admin client config: %v", err) } openshiftProjectAdminClient, err := testutil.CreateNewProject(openshiftClusterAdminClient, *openshiftClusterAdminClientConfig, testutil.Namespace(), "bob") if err != nil { t.Fatalf("error creating project: %v", err) } imageStream := &imageapi.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "test-image-stream"}} config := deploytest.OkDeploymentConfig(0) config.Namespace = testutil.Namespace() configWatch, err := openshiftProjectAdminClient.DeploymentConfigs(testutil.Namespace()).Watch(labels.Everything(), fields.Everything(), "0") if err != nil { t.Fatalf("Couldn't subscribe to Deployments %v", err) } defer configWatch.Stop() if imageStream, err = openshiftProjectAdminClient.ImageStreams(testutil.Namespace()).Create(imageStream); err != nil { t.Fatalf("Couldn't create ImageStream: %v", err) } imageWatch, err := openshiftProjectAdminClient.ImageStreams(testutil.Namespace()).Watch(labels.Everything(), fields.Everything(), "0") if err != nil { t.Fatalf("Couldn't subscribe to ImageStreams: %s", err) } defer imageWatch.Stop() // Make a function which can create a new tag event for the image stream and // then wait for the stream status to be asynchronously updated. createTagEvent := func(image string) { mapping := &imageapi.ImageStreamMapping{ ObjectMeta: kapi.ObjectMeta{Name: imageStream.Name}, Tag: "latest", Image: imageapi.Image{ ObjectMeta: kapi.ObjectMeta{ Name: image, }, DockerImageReference: fmt.Sprintf("registry:8080/openshift/test-image@%s", image), }, } if err := openshiftProjectAdminClient.ImageStreamMappings(testutil.Namespace()).Create(mapping); err != nil { t.Fatalf("unexpected error: %v", err) } t.Log("Waiting for image stream mapping to be reflected in the IS status...") statusLoop: for { select { case event := <-imageWatch.ResultChan(): stream := event.Object.(*imageapi.ImageStream) if _, ok := stream.Status.Tags["latest"]; ok { t.Logf("ImageStream %s now has Status with tags: %#v", stream.Name, stream.Status.Tags) break statusLoop } else { t.Logf("Still waiting for latest tag status on ImageStream %s", stream.Name) } } } } if config, err = openshiftProjectAdminClient.DeploymentConfigs(testutil.Namespace()).Create(config); err != nil { t.Fatalf("Couldn't create DeploymentConfig: %v", err) } createTagEvent("sha256:00000000000000000000000000000001") var newConfig *deployapi.DeploymentConfig t.Log("Waiting for a new deployment config in response to ImageStream update") waitForNewConfig: for { select { case event := <-configWatch.ResultChan(): if event.Type == watchapi.Modified { newConfig = event.Object.(*deployapi.DeploymentConfig) break waitForNewConfig } } } if e, a := 1, newConfig.LatestVersion; e != a { t.Fatalf("expected config version %d, got %d", e, a) } }
func TestPolicyCommands(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } const projectName = "hammer-project" haroldClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, projectName, "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } addViewer := policy.RoleModificationOptions{ RoleName: bootstrappolicy.ViewRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor(projectName, haroldClient), Users: []string{"valerie"}, Groups: []string{"my-group"}, } if err := addViewer.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } viewers, err := haroldClient.RoleBindings(projectName).Get("view") if err != nil { t.Fatalf("unexpected error: %v", err) } if !viewers.Users.Has("valerie") { t.Errorf("expected valerie in users: %v", viewers.Users) } if !viewers.Groups.Has("my-group") { t.Errorf("expected my-group in groups: %v", viewers.Groups) } removeValerie := policy.RemoveFromProjectOptions{ BindingNamespace: projectName, Client: haroldClient, Users: []string{"valerie"}, Out: ioutil.Discard, } if err := removeValerie.Run(); err != nil { t.Fatalf("unexpected error: %v", err) } viewers, err = haroldClient.RoleBindings(projectName).Get("view") if err != nil { t.Fatalf("unexpected error: %v", err) } if viewers.Users.Has("valerie") { t.Errorf("unexpected valerie in users: %v", viewers.Users) } if !viewers.Groups.Has("my-group") { t.Errorf("expected my-group in groups: %v", viewers.Groups) } removeMyGroup := policy.RemoveFromProjectOptions{ BindingNamespace: projectName, Client: haroldClient, Groups: []string{"my-group"}, Out: ioutil.Discard, } if err := removeMyGroup.Run(); err != nil { t.Fatalf("unexpected error: %v", err) } viewers, err = haroldClient.RoleBindings(projectName).Get("view") if err != nil { t.Fatalf("unexpected error: %v", err) } if viewers.Users.Has("valerie") { t.Errorf("unexpected valerie in users: %v", viewers.Users) } if viewers.Groups.Has("my-group") { t.Errorf("unexpected my-group in groups: %v", viewers.Groups) } }