All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.vespa.hosted.controller.notification.NotificationSource Maven / Gradle / Ivy

// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.notification;

import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.text.Text;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;

import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;

/**
 * Denotes the source of the notification.
 *
 * @author freva
 */
public class NotificationSource {
    private final TenantName tenant;
    private final Optional application;
    private final Optional instance;
    private final Optional zoneId;
    private final Optional clusterId;
    private final Optional jobType;
    private final OptionalLong runNumber;

    public NotificationSource(TenantName tenant, Optional application, Optional instance,
                              Optional zoneId, Optional clusterId, Optional jobType, OptionalLong runNumber) {
        this.tenant = Objects.requireNonNull(tenant, "tenant cannot be null");
        this.application = Objects.requireNonNull(application, "application cannot be null");
        this.instance = Objects.requireNonNull(instance, "instance cannot be null");
        this.zoneId = Objects.requireNonNull(zoneId, "zoneId cannot be null");
        this.clusterId = Objects.requireNonNull(clusterId, "clusterId cannot be null");
        this.jobType = Objects.requireNonNull(jobType, "jobType cannot be null");
        this.runNumber = Objects.requireNonNull(runNumber, "runNumber cannot be null");

        if (instance.isPresent() && application.isEmpty())
            throw new IllegalArgumentException("Application name must be present with instance name");
        if (zoneId.isPresent() && instance.isEmpty())
            throw new IllegalArgumentException("Instance name must be present with zone ID");
        if (clusterId.isPresent() && zoneId.isEmpty())
            throw new IllegalArgumentException("Zone ID must be present with cluster ID");
        if (clusterId.isPresent() && jobType.isPresent())
            throw new IllegalArgumentException("Cannot set both cluster ID and job type");
        if (jobType.isPresent() && instance.isEmpty())
            throw new IllegalArgumentException("Instance name must be present with job type");
        if (jobType.isPresent() != runNumber.isPresent())
            throw new IllegalArgumentException(Text.format("Run number (%s) must be 1-to-1 with job type (%s)",
                    runNumber.isPresent() ? "present" : "missing", jobType.map(i -> "present").orElse("missing")));
    }


    public TenantName tenant() { return tenant; }
    public Optional application() { return application; }
    public Optional instance() { return instance; }
    public Optional zoneId() { return zoneId; }
    public Optional clusterId() { return clusterId; }
    public Optional jobType() { return jobType; }
    public OptionalLong runNumber() { return runNumber; }

    /**
     * Returns true iff this source contains the given source. A source contains the other source if
     * all the set fields in this source are equal to the given source, while the fields not set
     * in this source are ignored.
     */
    public boolean contains(NotificationSource other) {
        return tenant.equals(other.tenant) &&
                (application.isEmpty() || application.equals(other.application)) &&
                (instance.isEmpty() || instance.equals(other.instance)) &&
                (zoneId.isEmpty() || zoneId.equals(other.zoneId)) &&
                (clusterId.isEmpty() || clusterId.equals(other.clusterId)) &&
                (jobType.isEmpty() || jobType.equals(other.jobType)); // Do not consider run number (it's unique!)
    }

    /**
     * Returns whether this source from a production deployment or deployment related to prod deployment (e.g. to
     * staging zone), or if this is at tenant or application level
     */
    public boolean isProduction() {
        return ! zoneId.map(ZoneId::environment)
                .or(() -> jobType.map(JobType::environment))
                .map(Environment::isManuallyDeployed)
                .orElse(false);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        NotificationSource that = (NotificationSource) o;
        return tenant.equals(that.tenant) && application.equals(that.application) && instance.equals(that.instance) &&
                zoneId.equals(that.zoneId) && clusterId.equals(that.clusterId) && jobType.equals(that.jobType); // Do not consider run number (it's unique!)
    }

    @Override
    public int hashCode() {
        return Objects.hash(tenant, application, instance, zoneId, clusterId, jobType, runNumber);
    }

    @Override
    public String toString() {
        return "NotificationSource{" +
                "tenant=" + tenant +
                application.map(application -> ", application=" + application.value()).orElse("") +
                instance.map(instance -> ", instance=" + instance.value()).orElse("") +
                zoneId.map(zoneId -> ", zone=" + zoneId.value()).orElse("") +
                clusterId.map(clusterId -> ", clusterId=" + clusterId.value()).orElse("") +
                jobType.map(jobType -> ", job=" + jobType.jobName() + "#" + runNumber.getAsLong()).orElse("") +
                '}';
    }

    private static NotificationSource from(TenantName tenant, ApplicationName application, InstanceName instance, ZoneId zoneId,
                                           ClusterSpec.Id clusterId, JobType jobType, Long runNumber) {
        return new NotificationSource(tenant, Optional.ofNullable(application), Optional.ofNullable(instance), Optional.ofNullable(zoneId),
                Optional.ofNullable(clusterId), Optional.ofNullable(jobType), runNumber == null ? OptionalLong.empty() : OptionalLong.of(runNumber));
    }

    public static NotificationSource from(TenantName tenantName) {
        return from(tenantName, null, null, null, null, null, null);
    }

    public static NotificationSource from(TenantAndApplicationId id) {
        return from(id.tenant(), id.application(), null, null, null, null, null);
    }

    public static NotificationSource from(ApplicationId app) {
        return from(app.tenant(), app.application(), app.instance(), null, null, null, null);
    }

    public static NotificationSource from(DeploymentId deploymentId) {
        ApplicationId app = deploymentId.applicationId();
        return from(app.tenant(), app.application(), app.instance(), deploymentId.zoneId(), null, null, null);
    }

    public static NotificationSource from(DeploymentId deploymentId, ClusterSpec.Id clusterId) {
        ApplicationId app = deploymentId.applicationId();
        return from(app.tenant(), app.application(), app.instance(), deploymentId.zoneId(), clusterId, null, null);
    }

    public static NotificationSource from(RunId runId) {
        ApplicationId app = runId.application();
        return from(app.tenant(), app.application(), app.instance(), null, null, runId.job().type(), runId.number());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy