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

com.netflix.spinnaker.igor.gcb.GoogleCloudBuildCache Maven / Gradle / Ivy

There is a newer version: 4.18.0
Show newest version
/*
 * Copyright 2019 Google, 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.spinnaker.igor.gcb;

import com.netflix.spinnaker.igor.polling.LockService;
import com.netflix.spinnaker.kork.jedis.RedisClientDelegate;
import java.time.Duration;
import java.util.Map;
import javax.annotation.Nullable;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * Cache to keep track of the status of Google Cloud builds. In general, this cache will be updated
 * as echo receives PubSub build notifications and sends them to igor.
 */
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@Slf4j
class GoogleCloudBuildCache {
  private static final int inProgressTtlSeconds = 60 * 10;
  private static final int completedTtlSeconds = 60 * 60 * 24;

  private final LockService lockService;
  private final RedisClientDelegate redisClientDelegate;
  private final String keyPrefix;
  private final String lockPrefix;

  @RequiredArgsConstructor(access = AccessLevel.PACKAGE)
  static class Factory {
    private final LockService lockService;
    private final RedisClientDelegate redisClientDelegate;
    private final String baseKeyPrefix;
    private static final String baseLockPrefix = "googleCloudBuild";

    GoogleCloudBuildCache create(String accountName) {
      String keyPrefix = String.format("%s:%s", baseKeyPrefix, accountName);
      String lockPrefix = String.format("%s.%s", baseLockPrefix, accountName);
      return new GoogleCloudBuildCache(lockService, redisClientDelegate, keyPrefix, lockPrefix);
    }
  }

  @Nullable
  String getBuild(String buildId) {
    String key = new GoogleCloudBuildKey(keyPrefix, buildId).toString();
    return redisClientDelegate.withCommandsClient(
        c -> {
          Map res = c.hgetAll(key);
          return res.get("build");
        });
  }

  private void internalUpdateBuild(String buildId, @Nullable String status, String build) {
    String key = new GoogleCloudBuildKey(keyPrefix, buildId).toString();
    redisClientDelegate.withCommandsClient(
        c -> {
          String oldStatus = c.hget(key, "status");
          if (allowUpdate(oldStatus, status)) {
            int ttlSeconds = getTtlSeconds(status);
            c.hset(key, "status", status);
            c.hset(key, "build", build);
            c.expire(key, ttlSeconds);
          }
        });
  }

  private int getTtlSeconds(@Nullable String statusString) {
    try {
      GoogleCloudBuildStatus status = GoogleCloudBuildStatus.valueOfNullable(statusString);
      if (status.isComplete()) {
        return completedTtlSeconds;
      } else {
        return inProgressTtlSeconds;
      }
    } catch (IllegalArgumentException e) {
      log.warn("Received unknown Google Cloud Build Status: {}", statusString);
      return inProgressTtlSeconds;
    }
  }

  // As we may be processing build notifications out of order, only allow an update of the cache if
  // the incoming build status is newer than the status that we currently have cached.
  private boolean allowUpdate(@Nullable String oldStatusString, @Nullable String newStatusString) {
    try {
      GoogleCloudBuildStatus oldStatus = GoogleCloudBuildStatus.valueOfNullable(oldStatusString);
      GoogleCloudBuildStatus newStatus = GoogleCloudBuildStatus.valueOfNullable(newStatusString);
      return newStatus.greaterThanOrEqualTo(oldStatus);
    } catch (IllegalArgumentException e) {
      // If one of the statuses is not recognized, allow the update (assuming that the later message
      // is newer). This is to be robust against GCB adding statuses in the future.
      return true;
    }
  }

  void updateBuild(String buildId, @Nullable String status, String build) {
    String lockName = String.format("%s.%s", lockPrefix, buildId);
    lockService.acquire(
        lockName, Duration.ofSeconds(10), () -> internalUpdateBuild(buildId, status, build));
  }

  @RequiredArgsConstructor(access = AccessLevel.PACKAGE)
  static final class GoogleCloudBuildKey {
    private final String prefix;
    private final String id;

    public String toString() {
      return String.format("%s:%s", prefix, id);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy