Skip to content

Commit

Permalink
[issue 183] - OpenShift AI: implement tooling to provision the Author…
Browse files Browse the repository at this point in the history
…ino service via its Operator
  • Loading branch information
fabiobrz committed Jul 19, 2024
1 parent d37b065 commit 2d08d40
Show file tree
Hide file tree
Showing 11 changed files with 5,767 additions and 0 deletions.
22 changes: 22 additions & 0 deletions core/src/main/java/org/jboss/intersmash/IntersmashConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ public class IntersmashConfig {
private static final String HYPERFOIL_OPERATOR_PACKAGE_MANIFEST = "intersmash.hyperfoil.operators.package_manifest";
private static final String COMMUNITY_HYPERFOIL_OPERATOR_PACKAGE_MANIFEST = "hyperfoil-bundle";
private static final String DEFAULT_HYPERFOIL_OPERATOR_PACKAGE_MANIFEST = COMMUNITY_HYPERFOIL_OPERATOR_PACKAGE_MANIFEST;
private static final String AUTHORINO_OPERATOR_CATALOG_SOURCE_NAME = "intersmash.authorino.operators.catalog_source";
private static final String AUTHORINO_OPERATOR_INDEX_IMAGE = "intersmash.authorino.operators.index_image";
private static final String AUTHORINO_OPERATOR_CHANNEL = "intersmash.authorino.operators.channel";
private static final String AUTHORINO_OPERATOR_PACKAGE_MANIFEST = "intersmash.authorino.operators.package_manifest";
private static final String COMMUNITY_AUTHORINO_OPERATOR_PACKAGE_MANIFEST = "authorino-operator";
private static final String DEFAULT_AUTHORINO_OPERATOR_PACKAGE_MANIFEST = COMMUNITY_AUTHORINO_OPERATOR_PACKAGE_MANIFEST;
// Bootable Jar
private static final String BOOTABLE_JAR_IMAGE_URL = "intersmash.bootable.jar.image";

Expand Down Expand Up @@ -228,6 +234,22 @@ public static String hyperfoilOperatorPackageManifest() {
return XTFConfig.get(HYPERFOIL_OPERATOR_PACKAGE_MANIFEST, DEFAULT_HYPERFOIL_OPERATOR_PACKAGE_MANIFEST);
}

public static String authorinoOperatorCatalogSource() {
return XTFConfig.get(AUTHORINO_OPERATOR_CATALOG_SOURCE_NAME, DEFAULT_OPERATOR_CATALOG_SOURCE_NAME);
}

public static String authorinoOperatorIndexImage() {
return XTFConfig.get(AUTHORINO_OPERATOR_INDEX_IMAGE);
}

public static String authorinoOperatorChannel() {
return XTFConfig.get(AUTHORINO_OPERATOR_CHANNEL);
}

public static String authorinoOperatorPackageManifest() {
return XTFConfig.get(AUTHORINO_OPERATOR_PACKAGE_MANIFEST, DEFAULT_AUTHORINO_OPERATOR_PACKAGE_MANIFEST);
}

public static String bootableJarImageURL() {
return XTFConfig.get(BOOTABLE_JAR_IMAGE_URL);
}
Expand Down
1 change: 1 addition & 0 deletions docs/Provisioner-by-Product.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The provisioner is selected by a factory, based on the _Application class_ type
| **Keycloak & Red Hat Build of Keycloak Operator** (see KeycloakOperatorApplication) | KeycloakOperatorProvisioner | :heavy_check_mark: | :heavy_check_mark: |
| **Red Hat SSO Operator** (see RhSsoOperatorApplication) - **DEPRECATED** | RhSsoOperatorProvisioner | :x: | :heavy_check_mark: |
| **WildFly & JBoss EAP 8 Operator** (see WildflyOperatorApplication) | WildflyOperatorProvisioner | :heavy_check_mark: | :heavy_check_mark: |
| **Authorino Operator** (see AuthorinoOperatorApplication) | AuthorinoOperatorProvisioner | :heavy_check_mark: | :heavy_check_mark: |
| **Template based services** | | ||
| **JBoss EAP 7 Legacy s2i Build Template** (see Eap7LegacyS2iBuildTemplateApplication) | Eap7LegacyS2iBuildTemplateProvisioner | :x: | :heavy_check_mark: |
| **JBoss EAP 7 Legacy s2i Deployment Template** (see Eap7TemplateOpenShiftApplication) | Eap7TemplateOpenShiftProvisioner | :x: | :heavy_check_mark: |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright (C) 2023 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 org.jboss.intersmash.application.openshift;

import java.util.List;

import io.kuadrant.authorino.operator.v1beta1.Authorino;
import io.kuadrant.authorino.v1beta2.AuthConfig;

/**
* End user Application interface which presents the Authorino operator service on OpenShift Container Platform.
*
* The application will be deployed by:
* <ul>
* <li>{@link org.jboss.intersmash.provision.openshift.AuthorinoOperatorProvisioner}</li>
* </ul>
*/
public interface AuthorinoOperatorApplication extends OperatorApplication {

Authorino getAuthorino();

List<AuthConfig> getAuthConfigs();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
/**
* Copyright (C) 2023 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 org.jboss.intersmash.provision.openshift;

import java.util.List;
import java.util.stream.Collectors;

import org.jboss.intersmash.IntersmashConfig;
import org.jboss.intersmash.application.openshift.AuthorinoOperatorApplication;
import org.jboss.intersmash.provision.openshift.operator.OperatorProvisioner;
import org.jboss.intersmash.provision.openshift.operator.authorino.AuthConfigList;
import org.jboss.intersmash.provision.openshift.operator.authorino.AuthorinoList;
import org.slf4j.event.Level;

import cz.xtf.core.config.OpenShiftConfig;
import cz.xtf.core.event.helpers.EventHelper;
import cz.xtf.core.openshift.OpenShiftWaiters;
import cz.xtf.core.openshift.OpenShifts;
import cz.xtf.core.waiting.SimpleWaiter;
import io.fabric8.kubernetes.api.model.DeletionPropagation;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext;
import io.kuadrant.authorino.operator.v1beta1.Authorino;
import io.kuadrant.authorino.v1beta2.AuthConfig;
import lombok.NonNull;

/**
* Authorino Operator based provisioner
*
* See: <br>
* - https://github.com/Kuadrant/authorino/blob/main/docs/architecture.md<br>
* - https://github.com/Kuadrant/authorino-operator
*/
public class AuthorinoOperatorProvisioner extends OperatorProvisioner<AuthorinoOperatorApplication> {
private final static String AUTHORINO_RESOURCE = "authorinos.operator.authorino.kuadrant.io";
private static NonNamespaceOperation<Authorino, AuthorinoList, Resource<Authorino>> AUTHORINOS_CLIENT;

private final static String AUTHCONFIG_RESOURCE = "authconfigs.authorino.kuadrant.io";
private static NonNamespaceOperation<AuthConfig, AuthConfigList, Resource<AuthConfig>> AUTHCONFIGS_CLIENT;

private static final String OPERATOR_ID = IntersmashConfig.authorinoOperatorPackageManifest();

public AuthorinoOperatorProvisioner(@NonNull AuthorinoOperatorApplication application) {
super(application, OPERATOR_ID);
}

public static String getOperatorId() {
return OPERATOR_ID;
}

/**
* Get a client capable of working with {@link #AUTHORINO_RESOURCE} custom resource.
*
* @return client for operations with {@link #AUTHORINO_RESOURCE} custom resource
*/
public NonNamespaceOperation<Authorino, AuthorinoList, Resource<Authorino>> authorinosClient() {
if (AUTHORINOS_CLIENT == null) {
CustomResourceDefinition crd = OpenShifts.admin().apiextensions().v1().customResourceDefinitions()
.withName(AUTHORINO_RESOURCE).get();
CustomResourceDefinitionContext crdc = CustomResourceDefinitionContext.fromCrd(crd);
if (!getCustomResourceDefinitions().contains(AUTHORINO_RESOURCE)) {
throw new RuntimeException(String.format("[%s] custom resource is not provided by [%s] operator.",
AUTHORINO_RESOURCE, OPERATOR_ID));
}
MixedOperation<Authorino, AuthorinoList, Resource<Authorino>> client = OpenShifts
.master().newHasMetadataOperation(crdc, Authorino.class, AuthorinoList.class);
AUTHORINOS_CLIENT = client.inNamespace(OpenShiftConfig.namespace());
}
return AUTHORINOS_CLIENT;
}

/**
* Get a reference to an {@link Authorino} custom resource. Use get() to get the actual object, or null in case it does not
* exist on tested cluster.
*
* @return A concrete {@link Resource} instance representing the {@link Authorino} resource definition
*/
public Resource<Authorino> authorino() {
return authorinosClient().withName(getApplication().getAuthorino().getMetadata().getName());
}

/**
* Get a client capable of working with {@link #AUTHCONFIG_RESOURCE} custom resource.
*
* @return client for operations with {@link #AUTHCONFIG_RESOURCE} custom resource
*/
public NonNamespaceOperation<AuthConfig, AuthConfigList, Resource<AuthConfig>> authConfigsClient() {
if (AUTHCONFIGS_CLIENT == null) {
CustomResourceDefinition crd = OpenShifts.admin().apiextensions().v1().customResourceDefinitions()
.withName(AUTHCONFIG_RESOURCE).get();
CustomResourceDefinitionContext crdc = CustomResourceDefinitionContext.fromCrd(crd);
if (!getCustomResourceDefinitions().contains(AUTHCONFIG_RESOURCE)) {
throw new RuntimeException(String.format("[%s] custom resource is not provided by [%s] operator.",
AUTHCONFIG_RESOURCE, OPERATOR_ID));
}
MixedOperation<AuthConfig, AuthConfigList, Resource<AuthConfig>> amqClient = OpenShifts
.master().newHasMetadataOperation(crdc, AuthConfig.class, AuthConfigList.class);
AUTHCONFIGS_CLIENT = amqClient.inNamespace(OpenShiftConfig.namespace());
}
return AUTHCONFIGS_CLIENT;
}

/**
* Get all {@link AuthConfig} custom resource instances maintained by the current operator instance.
*
* Be aware that this method return just a references to the CRs, they might not actually exist on the cluster.
* Use get() to get the actual object, or null in case it does not exist on tested cluster.
* @return A list of {@link Resource} instances representing the {@link AuthConfig} resource definitions
*/
public List<Resource<AuthConfig>> authConfigs() {
AuthorinoOperatorApplication application = getApplication();
return application.getAuthConfigs().stream()
.map(a -> a.getMetadata().getName())
.map(this::authConfig)
.collect(Collectors.toList());
}

/**
* Get a reference to a {@link AuthConfig} custom resource object.
* Use get() to get the actual object, or null in case it does not exist on tested cluster.
*
* @param name name of the {@link AuthConfig} custom resource
* @return A concrete {@link Resource} instance representing the {@link AuthConfig} resource definition
*/
public Resource<AuthConfig> authConfig(final String name) {
return authConfigsClient().withName(name);
}

@Override
public void deploy() {
ffCheck = FailFastUtils.getFailFastCheck(EventHelper.timeOfLastEventBMOrTestNamespaceOrEpoch(),
getApplication().getName());
subscribe();
final int replicas = getApplication().getAuthorino().getSpec().getReplicas();
// deploy the authorino CR
authorinosClient().createOrReplace(getApplication().getAuthorino());
// deploy AuthConfig CR(s)
getApplication().getAuthConfigs().stream()
.forEach(authConfig -> authConfigsClient().createOrReplace(authConfig));
// wait for authorino instance to be created...
new SimpleWaiter(() -> authorino().get() != null)
.failFast(ffCheck)
.level(Level.DEBUG)
.waitFor();
// and then ready
OpenShiftWaiters.get(OpenShiftProvisioner.openShift, ffCheck).areExactlyNPodsReady(replicas,
authorino().get().getKind(), getApplication().getAuthorino().getMetadata().getName())
.level(Level.DEBUG)
.waitFor();
// wait for AuthConfig CR(s) to be created
authConfigs()
.forEach(authConfig -> new SimpleWaiter(() -> authConfig.get() != null).level(Level.DEBUG).waitFor());
}

@Override
public void undeploy() {
// delete the AuthConfig CR(s)
authConfigs().forEach(authConfig -> authConfig.withPropagationPolicy(DeletionPropagation.FOREGROUND).delete());
// delete the Authorino CR, first scaling it down to 0 (graceful shutdown)
this.scale(0, Boolean.TRUE);
authorino().withPropagationPolicy(DeletionPropagation.FOREGROUND).delete();
// wait
new SimpleWaiter(() -> authorinosClient().list().getItems().size() == 0).failFast(ffCheck).level(Level.DEBUG)
.waitFor();
authConfigs().forEach(
authConfig -> new SimpleWaiter(() -> authConfig.get() == null).level(Level.DEBUG).failFast(ffCheck).waitFor());
// unsubscribe
unsubscribe();
}

@Override
public void scale(int replicas, boolean wait) {
Authorino authorino = authorino().get();
authorino.getSpec().setReplicas(replicas);
authorino().replace(authorino);
if (wait) {
OpenShiftWaiters.get(OpenShiftProvisioner.openShift, ffCheck)
.areExactlyNPodsReady(replicas, authorino.getKind(), authorino.getMetadata().getName())
.level(Level.DEBUG)
.waitFor();
}
}

/**
* Get the provisioned application service related Pods
* <p>
* Currently blocked by the fact that Pod Status pod names do not reflect the reality
* <p>
*
* @return A list of related {@link Pod} instances
*/
@Override
public List<Pod> getPods() {
return OpenShiftProvisioner.openShift.getLabeledPods("authorino-resource", getApplication().getName());
}

@Override
protected String getOperatorCatalogSource() {
return IntersmashConfig.authorinoOperatorCatalogSource();
}

@Override
protected String getOperatorIndexImage() {
return IntersmashConfig.authorinoOperatorIndexImage();
}

@Override
protected String getOperatorChannel() {
return IntersmashConfig.authorinoOperatorChannel();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright (C) 2023 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 org.jboss.intersmash.provision.openshift;

import org.jboss.intersmash.application.Application;
import org.jboss.intersmash.application.openshift.AuthorinoOperatorApplication;
import org.jboss.intersmash.provision.ProvisionerFactory;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class AuthorinoOperatorProvisionerFactory implements ProvisionerFactory<AuthorinoOperatorProvisioner> {

@Override
public AuthorinoOperatorProvisioner getProvisioner(Application application) {
if (AuthorinoOperatorApplication.class.isAssignableFrom(application.getClass()))
return new AuthorinoOperatorProvisioner((AuthorinoOperatorApplication) application);
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (C) 2023 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 org.jboss.intersmash.provision.openshift.operator.authorino;

import io.fabric8.kubernetes.client.CustomResourceList;
import io.kuadrant.authorino.v1beta2.AuthConfig;

public class AuthConfigList extends CustomResourceList<AuthConfig> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (C) 2023 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 org.jboss.intersmash.provision.openshift.operator.authorino;

import io.fabric8.kubernetes.client.CustomResourceList;
import io.kuadrant.authorino.operator.v1beta1.Authorino;

public class AuthorinoList extends CustomResourceList<Authorino> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ org.jboss.intersmash.provision.openshift.Eap7ImageOpenShiftProvisionerFactory
org.jboss.intersmash.provision.openshift.Eap7LegacyS2iBuildTemplateProvisionerFactory
org.jboss.intersmash.provision.openshift.Eap7TemplateOpenShiftProvisionerFactory
org.jboss.intersmash.provision.openshift.HyperfoilOperatorProvisionerFactory
org.jboss.intersmash.provision.openshift.AuthorinoOperatorProvisionerFactory

Loading

0 comments on commit 2d08d40

Please sign in to comment.