DatabaseServiceDeployable.java

package org.entando.kubernetes.controller.databaseservice;

import static java.lang.String.format;
import static org.entando.kubernetes.controller.databaseservice.EntandoDatabaseServiceHelper.strategyFor;

import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.extensions.Ingress;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.entando.kubernetes.controller.spi.common.DbmsDockerVendorStrategy;
import org.entando.kubernetes.controller.spi.common.NameUtils;
import org.entando.kubernetes.controller.spi.common.SecretUtils;
import org.entando.kubernetes.controller.spi.container.DeployableContainer;
import org.entando.kubernetes.controller.spi.deployable.Deployable;
import org.entando.kubernetes.controller.spi.deployable.ExternalService;
import org.entando.kubernetes.controller.spi.deployable.Secretive;
import org.entando.kubernetes.model.common.EntandoCustomResource;
import org.entando.kubernetes.model.externaldatabase.EntandoDatabaseService;

public class DatabaseServiceDeployable implements Deployable<DatabaseDeploymentResult>, Secretive {

    private final EntandoDatabaseService newEntandoDatabaseService;
    private final DbmsDockerVendorStrategy dbmsVendor;
    private final EntandoCustomResource customResource;
    protected List<DeployableContainer> containers;

    public DatabaseServiceDeployable(EntandoDatabaseService newEntandoDatabaseService) {
        this.dbmsVendor = strategyFor(newEntandoDatabaseService);
        this.customResource = newEntandoDatabaseService;
        if (EntandoDatabaseServiceHelper.deployDirectly(newEntandoDatabaseService)) {
            this.containers = Collections
                    .singletonList(new DatabaseServiceContainer(newEntandoDatabaseService,
                            buildVariableInitializer(strategyFor(newEntandoDatabaseService)),
                            strategyFor(newEntandoDatabaseService),
                            getPortOverride(newEntandoDatabaseService)));
        } else {
            this.containers = Collections.emptyList();
        }
        this.newEntandoDatabaseService = newEntandoDatabaseService;
    }

    @Override
    public Optional<ExternalService> getExternalService() {
        if (EntandoDatabaseServiceHelper.deployDirectly(newEntandoDatabaseService)) {
            return Optional.empty();
        } else {
            return Optional.of(new ExternalDatabaseService(newEntandoDatabaseService));
        }
    }

    private static Integer getPortOverride(EntandoDatabaseService newEntandoDatabaseService) {
        return newEntandoDatabaseService.getSpec().getPort().orElse(null);
    }

    protected String getDatabaseAdminSecretName() {
        return newEntandoDatabaseService.getSpec().getSecretName().orElse(NameUtils.standardAdminSecretName(newEntandoDatabaseService));
    }

    @Override
    public List<Secret> getSecrets() {
        if (newEntandoDatabaseService.getSpec().getSecretName().isPresent()) {
            //because it already exists
            return Collections.emptyList();
        } else {
            Secret secret = SecretUtils.generateSecret(
                    customResource,
                    getDatabaseAdminSecretName(),
                    dbmsVendor.getDefaultAdminUsername()
            );
            return Collections.singletonList(secret);
        }
    }

    protected String getDatabaseName() {
        return newEntandoDatabaseService.getSpec().getDatabaseName().orElse(
                NameUtils.databaseCompliantName(customResource, NameUtils.DB_NAME_QUALIFIER, dbmsVendor.getVendorConfig()));
    }

    @Override
    public String getServiceAccountToUse() {
        return getDefaultServiceAccountName();
    }

    protected DatabaseVariableInitializer buildVariableInitializer(DbmsDockerVendorStrategy vendorStrategy) {
        switch (vendorStrategy) {
            case CENTOS_MYSQL:
            case RHEL_MYSQL:
                return vars ->
                        //No DB creation. Dbs are created during schema creation
                        vars.add(new EnvVar("MYSQL_ROOT_PASSWORD", null,
                                SecretUtils.secretKeyRef(getDatabaseAdminSecretName(), SecretUtils.PASSSWORD_KEY)));
            case CENTOS_POSTGRESQL:
            case RHEL_POSTGRESQL:
                return vars -> {
                    vars.add(new EnvVar("POSTGRESQL_DATABASE", getDatabaseName(), null));
                    // This username will not be used, as we will be creating schema/user pairs,
                    // but without it the DB isn't created.
                    vars.add(new EnvVar("POSTGRESQL_USER", getDatabaseName() + "_user", null));
                    vars.add(new EnvVar("POSTGRESQL_PASSWORD", null,
                            SecretUtils.secretKeyRef(getDatabaseAdminSecretName(), SecretUtils.PASSSWORD_KEY)));
                    vars.add(new EnvVar("POSTGRESQL_ADMIN_PASSWORD", null,
                            SecretUtils.secretKeyRef(getDatabaseAdminSecretName(), SecretUtils.PASSSWORD_KEY)));

                };
            default:
                throw new IllegalStateException(
                        format("The DBMS %s not supported for containerized deployments", vendorStrategy.getName()));
        }
    }

    @Override
    public Optional<Long> getFileSystemUserAndGroupId() {
        return dbmsVendor.getFileSystemUserGroupid();
    }

    @Override
    public List<DeployableContainer> getContainers() {
        return containers;
    }

    @Override
    public Optional<String> getQualifier() {
        return Optional.empty();
    }

    @Override
    public EntandoCustomResource getCustomResource() {
        return customResource;
    }

    @Override
    public DatabaseDeploymentResult createResult(Deployment deployment, Service service, Ingress ingress, Pod pod) {
        return new DatabaseDeploymentResult(service, dbmsVendor.getVendorConfig(), getDatabaseName(), getDatabaseAdminSecretName(), pod);
    }
}