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

com.netflix.genie.web.agent.services.impl.AgentJobServiceImpl Maven / Gradle / Ivy

The newest version!
/*
 *
 *  Copyright 2018 Netflix, 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 com.netflix.genie.web.agent.services.impl;

import com.google.common.collect.Sets;
import com.netflix.genie.common.internal.dtos.AgentClientMetadata;
import com.netflix.genie.common.internal.dtos.ArchiveStatus;
import com.netflix.genie.common.internal.dtos.JobRequest;
import com.netflix.genie.common.internal.dtos.JobRequestMetadata;
import com.netflix.genie.common.internal.dtos.JobSpecification;
import com.netflix.genie.common.internal.dtos.JobStatus;
import com.netflix.genie.common.internal.exceptions.checked.GenieJobResolutionException;
import com.netflix.genie.common.internal.exceptions.unchecked.GenieAgentRejectedException;
import com.netflix.genie.common.internal.exceptions.unchecked.GenieIdAlreadyExistsException;
import com.netflix.genie.common.internal.exceptions.unchecked.GenieJobNotFoundException;
import com.netflix.genie.common.internal.exceptions.unchecked.GenieJobResolutionRuntimeException;
import com.netflix.genie.common.internal.exceptions.unchecked.GenieJobSpecificationNotFoundException;
import com.netflix.genie.web.agent.inspectors.InspectionReport;
import com.netflix.genie.web.agent.services.AgentConfigurationService;
import com.netflix.genie.web.agent.services.AgentFilterService;
import com.netflix.genie.web.agent.services.AgentJobService;
import com.netflix.genie.web.data.services.DataServices;
import com.netflix.genie.web.data.services.PersistenceService;
import com.netflix.genie.web.dtos.JobSubmission;
import com.netflix.genie.web.dtos.ResolvedJob;
import com.netflix.genie.web.exceptions.checked.IdAlreadyExistsException;
import com.netflix.genie.web.exceptions.checked.NotFoundException;
import com.netflix.genie.web.services.JobResolverService;
import com.netflix.genie.web.util.MetricsUtils;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Nullable;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.util.HashSet;
import java.util.Map;
import java.util.UUID;

/**
 * Default implementation of {@link AgentJobService}.
 *
 * @author tgianos
 * @since 4.0.0
 */
@Validated
@Transactional
public class AgentJobServiceImpl implements AgentJobService {

    private static final String AGENT_JOB_SERVICE_METRIC_PREFIX = "genie.services.agentJob.";
    private static final String HANDSHAKE_COUNTER_METRIC_NAME = AGENT_JOB_SERVICE_METRIC_PREFIX + "handshake.counter";
    private static final String GET_AGENT_PROPERTIES_COUNTER_METRIC_NAME
        = AGENT_JOB_SERVICE_METRIC_PREFIX + "getAgentProperties.counter";
    private static final String AGENT_VERSION_METRIC_TAG_NAME = "agentVersion";
    private static final String HANDSHAKE_DECISION_METRIC_TAG_NAME = "handshakeDecision";
    private final PersistenceService persistenceService;
    private final JobResolverService jobResolverService;
    private final AgentFilterService agentFilterService;
    private final AgentConfigurationService agentConfigurationService;
    private final MeterRegistry meterRegistry;

    /**
     * Constructor.
     *
     * @param dataServices              The {@link DataServices} instance to use
     * @param jobResolverService        The specification service to use
     * @param agentFilterService        The agent filter service to use
     * @param agentConfigurationService The agent configuration service
     * @param meterRegistry             The metrics registry to use
     */
    public AgentJobServiceImpl(
        final DataServices dataServices,
        final JobResolverService jobResolverService,
        final AgentFilterService agentFilterService,
        final AgentConfigurationService agentConfigurationService, final MeterRegistry meterRegistry
    ) {
        this.persistenceService = dataServices.getPersistenceService();
        this.jobResolverService = jobResolverService;
        this.agentFilterService = agentFilterService;
        this.agentConfigurationService = agentConfigurationService;
        this.meterRegistry = meterRegistry;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void handshake(
        @Valid final AgentClientMetadata agentClientMetadata
    ) throws GenieAgentRejectedException {

        final HashSet tags = Sets.newHashSet(
            Tag.of(AGENT_VERSION_METRIC_TAG_NAME, agentClientMetadata.getVersion().orElse("null"))
        );

        final InspectionReport report;
        try {
            report = agentFilterService.inspectAgentMetadata(agentClientMetadata);
        } catch (final Exception e) {
            MetricsUtils.addFailureTagsWithException(tags, e);
            meterRegistry.counter(HANDSHAKE_COUNTER_METRIC_NAME, tags).increment();
            throw e;
        }

        MetricsUtils.addSuccessTags(tags);
        tags.add(Tag.of(HANDSHAKE_DECISION_METRIC_TAG_NAME, report.getDecision().name()));
        meterRegistry.counter(HANDSHAKE_COUNTER_METRIC_NAME, tags).increment();

        if (report.getDecision() == InspectionReport.Decision.REJECT) {
            throw new GenieAgentRejectedException("Agent rejected: " + report.getMessage());
        }

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Map getAgentProperties(
        @Valid final AgentClientMetadata agentClientMetadata
    ) {
        final HashSet tags = Sets.newHashSet(
            Tag.of(AGENT_VERSION_METRIC_TAG_NAME, agentClientMetadata.getVersion().orElse("null"))
        );

        try {
            final Map agentPropertiesMap = this.agentConfigurationService.getAgentProperties();
            MetricsUtils.addSuccessTags(tags);
            return agentPropertiesMap;
        } catch (final Exception e) {
            MetricsUtils.addFailureTagsWithException(tags, e);
            throw e;
        } finally {
            this.meterRegistry.counter(GET_AGENT_PROPERTIES_COUNTER_METRIC_NAME, tags).increment();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String reserveJobId(
        @Valid final JobRequest jobRequest,
        @Valid final AgentClientMetadata agentClientMetadata
    ) {
        final JobRequestMetadata jobRequestMetadata = new JobRequestMetadata(null, agentClientMetadata, 0, 0, null);
        try {
            return this.persistenceService.saveJobSubmission(
                new JobSubmission.Builder(jobRequest, jobRequestMetadata).build()
            );
        } catch (final IdAlreadyExistsException e) {
            // TODO: How to handle this?
            throw new GenieIdAlreadyExistsException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JobSpecification resolveJobSpecification(
        @NotBlank final String id
    ) throws GenieJobResolutionException, GenieJobResolutionRuntimeException {
        try {
            final JobRequest jobRequest = this.persistenceService.getJobRequest(id);
            final ResolvedJob resolvedJob = this.jobResolverService.resolveJob(id, jobRequest, false);
            this.persistenceService.saveResolvedJob(id, resolvedJob);
            return resolvedJob.getJobSpecification();
        } catch (final NotFoundException e) {
            throw new GenieJobResolutionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(readOnly = true)
    public JobSpecification getJobSpecification(@NotBlank final String id) {
        try {
            return this.persistenceService
                .getJobSpecification(id)
                .orElseThrow(
                    () -> new GenieJobSpecificationNotFoundException(
                        "No job specification exists for job with id " + id
                    )
                );
        } catch (final NotFoundException e) {
            throw new GenieJobNotFoundException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(readOnly = true)
    public JobSpecification dryRunJobSpecificationResolution(
        @Valid final JobRequest jobRequest
    ) throws GenieJobResolutionException {
        return this.jobResolverService.resolveJob(
            jobRequest.getRequestedId().orElse(UUID.randomUUID().toString()),
            jobRequest,
            false
        ).getJobSpecification();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void claimJob(@NotBlank final String id, @Valid final AgentClientMetadata agentClientMetadata) {
        try {
            this.persistenceService.claimJob(id, agentClientMetadata);
        } catch (final NotFoundException e) {
            throw new GenieJobNotFoundException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void updateJobStatus(
        @NotBlank final String id,
        final JobStatus currentStatus,
        final JobStatus newStatus,
        @Nullable final String newStatusMessage
    ) {
        try {
            this.persistenceService.updateJobStatus(id, currentStatus, newStatus, newStatusMessage);
        } catch (final NotFoundException e) {
            throw new GenieJobNotFoundException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JobStatus getJobStatus(@NotBlank final String id) {
        try {
            return this.persistenceService.getJobStatus(id);
        } catch (final NotFoundException e) {
            throw new GenieJobNotFoundException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void updateJobArchiveStatus(@NotBlank final String id, final ArchiveStatus newArchiveStatus) {
        try {
            this.persistenceService.updateJobArchiveStatus(id, newArchiveStatus);
        } catch (NotFoundException e) {
            throw new GenieJobNotFoundException(e);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy