Skip to content

Commit

Permalink
Fix fabric8io#2311: Add Support for creating bootstrap project template
Browse files Browse the repository at this point in the history
Just like `oc adm create-bootstrap-project-template` generates a template yaml for
project request resources to be created; OpenShiftClient should be able to
generate + Apply resources based upon project request name, description, display name,
requesting user and admin user. It should create the following resources:

- Project (actual project to be created)
- RoleBinding system:deployers
- RoleBinding system:image-builders
- RoleBinding system:image-pullers
- RoleBinding admin
  • Loading branch information
rohanKanojia committed Aug 10, 2020
1 parent d13a864 commit b6a3eed
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -15,6 +15,7 @@
* Fix #2292: Update createOrReplace to do replace when create fails with conflict

#### New Features
* Fix #2311: Add Support for creating bootstrap project template
* Fix #2287: Add support for V1 and V1Beta1 CustomResourceDefinition
* Fix #2319: Create Config without using auto-configure functionality or setting env variables

Expand Down
Expand Up @@ -16,6 +16,8 @@

package io.fabric8.openshift.client.server.mock;

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.rbac.RoleBindingBuilder;
import io.fabric8.openshift.api.model.Project;
import io.fabric8.openshift.api.model.ProjectBuilder;
import io.fabric8.openshift.api.model.ProjectList;
Expand All @@ -26,6 +28,9 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport;

import java.net.HttpURLConnection;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
Expand Down Expand Up @@ -93,4 +98,107 @@ public void testDelete() {
deleted = client.projects().withName("project3").delete();
assertFalse(deleted);
}

@Test
void testCreateProjectAndRoleBindings() {
// Given
String name = "test-project";
String displayName = "test-project";
String description = "test project";
String requestingUser = "request-user";
String adminUser = "admin-user";
server.expect().post().withPath("/apis/project.openshift.io/v1/projects")
.andReturn(HttpURLConnection.HTTP_CREATED, new ProjectBuilder()
.withNewMetadata()
.addToAnnotations("openshift.io/description", description)
.addToAnnotations("openshift.io/display-name", displayName)
.addToAnnotations("openshift.io/requester", requestingUser)
.withName("test-project")
.endMetadata()
.build()).once();
server.expect().post().withPath("/apis/rbac.authorization.k8s.io/v1/namespaces/test-project/rolebindings")
.andReturn(HttpURLConnection.HTTP_CREATED, new RoleBindingBuilder()
.withNewMetadata()
.addToAnnotations("openshift.io/description", "Allows all pods in this namespace to pull images from this namespace. It is auto-managed by a controller; remove subjects to disable.")
.withName("system:image-pullers")
.withNamespace(name)
.endMetadata()
.withNewRoleRef()
.withApiGroup("rbac.authorization.k8s.io")
.withKind("ClusterRole")
.withName("system:image-puller")
.endRoleRef()
.addNewSubject()
.withApiGroup("rbac.authorization.k8s.io")
.withKind("Group")
.withName("system:serviceaccounts:" + name)
.endSubject()
.build()).once();
server.expect().post().withPath("/apis/rbac.authorization.k8s.io/v1/namespaces/test-project/rolebindings")
.andReturn(HttpURLConnection.HTTP_CREATED, new RoleBindingBuilder()
.withNewMetadata()
.addToAnnotations("openshift.io/description", "Allows builds in this namespace to push images to" +
"this namespace. It is auto-managed by a controller; remove subjects to disable.")
.withName("system:image-builders")
.withNamespace(name)
.endMetadata()
.withNewRoleRef()
.withApiGroup("rbac.authorization.k8s.io")
.withKind("ClusterRole")
.withName("system:image-builder")
.endRoleRef()
.addNewSubject()
.withKind("ServiceAccount")
.withName("builder")
.withNamespace(name)
.endSubject()
.build()).once();
server.expect().post().withPath("/apis/rbac.authorization.k8s.io/v1/namespaces/test-project/rolebindings")
.andReturn(HttpURLConnection.HTTP_CREATED, new RoleBindingBuilder()
.withNewMetadata()
.addToAnnotations("openshift.io/description", " Allows deploymentconfigs in this namespace to rollout" +
" pods in this namespace. It is auto-managed by a controller; remove subjects" +
" to disable.")
.withName("system:deployers")
.withNamespace(name)
.endMetadata()
.withNewRoleRef()
.withApiGroup("rbac.authorization.k8s.io")
.withKind("ClusterRole")
.withName("system:deployer")
.endRoleRef()
.addNewSubject()
.withKind("ServiceAccount")
.withName("deployer")
.withNamespace(name)
.endSubject()
.build()).once();
server.expect().post().withPath("/apis/rbac.authorization.k8s.io/v1/namespaces/test-project/rolebindings")
.andReturn(HttpURLConnection.HTTP_CREATED, new RoleBindingBuilder()
.withNewMetadata()
.withName("admin")
.withNamespace(name)
.endMetadata()
.withNewRoleRef()
.withApiGroup("rbac.authorization.k8s.io")
.withKind("ClusterRole")
.withName("admin")
.endRoleRef()
.addNewSubject()
.withApiGroup("rbac.authorization.k8s.io")
.withKind("User")
.withName(adminUser)
.endSubject()
.build()).once();


OpenShiftClient client = server.getOpenshiftClient();

// When
List<HasMetadata> result = client.projects().createProjectAndRoleBindings(name, description, displayName, adminUser, requestingUser);

// Then
assertNotNull(result);
assertEquals(5, result.size());
}
}
Expand Up @@ -434,7 +434,7 @@ public NonNamespaceOperation<OAuthClient, OAuthClientList, DoneableOAuthClient,
}

@Override
public NonNamespaceOperation<Project, ProjectList, DoneableProject, Resource<Project, DoneableProject>> projects() {
public ProjectOperation projects() {
return new ProjectOperationsImpl(httpClient, OpenShiftConfig.wrap(getConfiguration()));
}

Expand Down
Expand Up @@ -85,7 +85,7 @@ public interface OpenShiftClient extends KubernetesClient {

NonNamespaceOperation<OAuthClient, OAuthClientList, DoneableOAuthClient, Resource<OAuthClient, DoneableOAuthClient>> oAuthClients();

NonNamespaceOperation<Project, ProjectList, DoneableProject, Resource<Project, DoneableProject>> projects();
ProjectOperation projects();

ProjectRequestOperation projectrequests();

Expand Down
@@ -0,0 +1,39 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* 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 io.fabric8.openshift.client.dsl;

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
import io.fabric8.openshift.api.model.DoneableProject;
import io.fabric8.openshift.api.model.Project;
import io.fabric8.openshift.api.model.ProjectList;

import java.util.List;

public interface ProjectOperation extends NonNamespaceOperation<Project, ProjectList, DoneableProject, Resource<Project, DoneableProject>> {
/**
* Creating Bootstrap Project Template
*
* @param name project name
* @param description project description
* @param displayName project display name
* @param adminUser project admin user
* @param requestingUser project requesting user
* @return list of items created
*/
List<HasMetadata> createProjectAndRoleBindings(String name, String description, String displayName, String adminUser, String requestingUser);
}
Expand Up @@ -24,6 +24,4 @@
public interface ProjectRequestOperation extends
Createable<ProjectRequest, ProjectRequest, DoneableProjectRequest>,
Listable<Status> {


}
Expand Up @@ -15,21 +15,34 @@
*/
package io.fabric8.openshift.client.dsl.internal;

import io.fabric8.kubernetes.api.model.DeletionPropagation;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
import io.fabric8.kubernetes.api.model.rbac.RoleBindingBuilder;
import io.fabric8.kubernetes.client.dsl.Resource;
import io.fabric8.kubernetes.client.dsl.base.OperationContext;
import io.fabric8.kubernetes.client.dsl.internal.NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl;
import io.fabric8.openshift.api.model.ProjectBuilder;
import io.fabric8.openshift.client.dsl.ProjectOperation;
import okhttp3.OkHttpClient;
import io.fabric8.openshift.api.model.DoneableProject;
import io.fabric8.openshift.api.model.Project;
import io.fabric8.openshift.api.model.ProjectList;
import io.fabric8.openshift.client.OpenShiftConfig;

import java.util.Map;
import java.util.TreeMap;
import java.util.ArrayList;
import java.util.List;

import static io.fabric8.openshift.client.OpenShiftAPIGroups.PROJECT;

public class ProjectOperationsImpl extends OpenShiftOperation<Project, ProjectList, DoneableProject,
Resource<Project, DoneableProject>> {
Resource<Project, DoneableProject>> implements ProjectOperation {
public static final String OPENSHIFT_IO_DESCRIPTION_ANNOTATION = "openshift.io/description";
public static final String OPENSHIFT_IO_DISPLAY_NAME_ANNOTATION = "openshift.io/display-name";
public static final String OPENSHIFT_IO_REQUESTER_ANNOTATION = "openshift.io/requester";
public static final String RBAC_AUTHORIZATION_APIGROUP = "rbac.authorization.k8s.io";
public static final String CLUSTER_ROLE = "ClusterRole";


public ProjectOperationsImpl(OkHttpClient client, OpenShiftConfig config) {
this(new OperationContext().withOkhttpClient(client).withConfig(config));
Expand All @@ -51,4 +64,113 @@ public ProjectOperationsImpl newInstance(OperationContext context) {
public boolean isResourceNamespaced() {
return false;
}

@Override
public List<HasMetadata> createProjectAndRoleBindings(String name, String description, String displayName, String adminUser, String requestingUser) {
List<HasMetadata> result = new ArrayList<>();
Project project = initProject(name, description, displayName, requestingUser);
List<HasMetadata> projectRoleBindings = initRoleBindings(name, adminUser);

// Create Project
result.add(create(project));

// Create Role Bindings
NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl listOp = new NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl(client, config, getNamespace(), null, false, false, new ArrayList<>(), projectRoleBindings, null, DeletionPropagation.BACKGROUND, true) {};
result.addAll(listOp.createOrReplace());

return result;
}

private Project initProject(String name, String description, String displayName, String requestingUser) {
return new ProjectBuilder()
.withNewMetadata()
.addToAnnotations(OPENSHIFT_IO_DESCRIPTION_ANNOTATION, description)
.addToAnnotations(OPENSHIFT_IO_DISPLAY_NAME_ANNOTATION, displayName)
.addToAnnotations(OPENSHIFT_IO_REQUESTER_ANNOTATION, requestingUser)
.withName(name)
.endMetadata()
.build();
}

private List<HasMetadata> initRoleBindings(String name, String adminUser) {
RoleBinding roleBindingPuller = new RoleBindingBuilder()
.withNewMetadata()
.addToAnnotations(OPENSHIFT_IO_DESCRIPTION_ANNOTATION, "Allows all pods in this namespace to pull images from this namespace. It is auto-managed by a controller; remove subjects to disable.")
.withName("system:image-pullers")
.withNamespace(name)
.endMetadata()
.withNewRoleRef()
.withApiGroup(RBAC_AUTHORIZATION_APIGROUP)
.withKind(CLUSTER_ROLE)
.withName("system:image-puller")
.endRoleRef()
.addNewSubject()
.withApiGroup(RBAC_AUTHORIZATION_APIGROUP)
.withKind("Group")
.withName("system:serviceaccounts:" + name)
.endSubject()
.build();
RoleBinding roleBindingBuilder = new RoleBindingBuilder()
.withNewMetadata()
.addToAnnotations(OPENSHIFT_IO_DESCRIPTION_ANNOTATION, "Allows builds in this namespace to push images to" +
"this namespace. It is auto-managed by a controller; remove subjects to disable.")
.withName("system:image-builders")
.withNamespace(name)
.endMetadata()
.withNewRoleRef()
.withApiGroup(RBAC_AUTHORIZATION_APIGROUP)
.withKind(CLUSTER_ROLE)
.withName("system:image-builder")
.endRoleRef()
.addNewSubject()
.withKind("ServiceAccount")
.withName("builder")
.withNamespace(name)
.endSubject()
.build();
RoleBinding roleBindingDeployer = new RoleBindingBuilder()
.withNewMetadata()
.addToAnnotations(OPENSHIFT_IO_DESCRIPTION_ANNOTATION, " Allows deploymentconfigs in this namespace to rollout" +
" pods in this namespace. It is auto-managed by a controller; remove subjects" +
" to disable.")
.withName("system:deployers")
.withNamespace(name)
.endMetadata()
.withNewRoleRef()
.withApiGroup(RBAC_AUTHORIZATION_APIGROUP)
.withKind(CLUSTER_ROLE)
.withName("system:deployer")
.endRoleRef()
.addNewSubject()
.withKind("ServiceAccount")
.withName("deployer")
.withNamespace(name)
.endSubject()
.build();

RoleBinding roleBindingAdmin = new RoleBindingBuilder()
.withNewMetadata()
.withName("admin")
.withNamespace(name)
.endMetadata()
.withNewRoleRef()
.withApiGroup(RBAC_AUTHORIZATION_APIGROUP)
.withKind(CLUSTER_ROLE)
.withName("admin")
.endRoleRef()
.addNewSubject()
.withApiGroup(RBAC_AUTHORIZATION_APIGROUP)
.withKind("User")
.withName(adminUser)
.endSubject()
.build();

List<HasMetadata> resources = new ArrayList<>();
resources.add(roleBindingPuller);
resources.add(roleBindingBuilder);
resources.add(roleBindingDeployer);
resources.add(roleBindingAdmin);

return resources;
}
}
Expand Up @@ -17,20 +17,23 @@

import io.fabric8.kubernetes.api.model.ListOptions;
import io.fabric8.kubernetes.api.model.ListOptionsBuilder;
import io.fabric8.kubernetes.client.dsl.base.OperationContext;
import io.fabric8.kubernetes.client.utils.URLUtils;
import io.fabric8.kubernetes.client.utils.Utils;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import io.fabric8.kubernetes.api.model.Status;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.dsl.base.OperationContext;
import io.fabric8.kubernetes.client.dsl.base.OperationSupport;
import io.fabric8.kubernetes.client.utils.Utils;
import io.fabric8.openshift.api.model.DoneableProjectRequest;
import io.fabric8.openshift.api.model.ProjectRequest;
import io.fabric8.openshift.client.OpenShiftConfig;
import io.fabric8.openshift.client.dsl.ProjectRequestOperation;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.ExecutionException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
Expand Down
Expand Up @@ -233,7 +233,7 @@ public NonNamespaceOperation<OAuthClient, OAuthClientList, DoneableOAuthClient,
}

@Override
public NonNamespaceOperation<Project, ProjectList, DoneableProject, Resource<Project, DoneableProject>> projects() {
public ProjectOperation projects() {
return delegate.projects();
}

Expand Down

0 comments on commit b6a3eed

Please sign in to comment.