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

com.netflix.spinnaker.clouddriver.google.GoogleExecutor.groovy Maven / Gradle / Ivy

There is a newer version: 5.89.0
Show newest version
/*
 * Copyright 2017 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.clouddriver.google

import com.google.api.client.googleapis.services.AbstractGoogleClientRequest
import com.google.api.client.http.HttpResponseException
import com.netflix.spectator.api.Clock
import com.netflix.spectator.api.Registry
import com.netflix.spinnaker.clouddriver.google.batch.GoogleBatchRequest
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component

import javax.annotation.PostConstruct
import java.util.concurrent.TimeUnit

/**
 * Provides a static-ish means to wrap API execution calls with spectator metrics.
 *
 * Spring makes this ugly.
 *
 * Normally there could be a "traits" providing this behavior with the class.
 * However traits are for instances not classes. Static class methods making API calls
 * are not instances. Thus we need to access this statically.
 * The registry throws a wrench into this because it is typically autowired in, however
 * autowiring is for instances not classes and static methods are on classes not instances.
 *
 * So this class provides a static registry that those static methods can use (traditionally
 * the calling class autowires its registry, but that isnt available). In order to implement
 * that static registry we "secretly" autowire it into a class instance then use the
 * PostConstruct method to bind the class static to the autowired registry.
 *
 * Did I mention Spring makes this ugly? It gets worse.
 *
 * The spring autowiring and initialization is random. So if that static method that this
 * is supporting wants to make a call during its initialization (e.g. calls made during
 * the GoogleCredentials initialization in clouddriver-google) then this module wont
 * necessarily be initialized yet. So the calling module needs to ensure this module is
 * first initialized by autowiring a GoogleExecutor even though it doesnt really need or
 * use the executor instance itself. Hence the @Component annotation.
 *
 * Ugly, but the Spring patterns and style are firmly entrenched within Spinnaker
 * so "when in Rome..."
 */
@Component
class GoogleExecutor {
  static Registry globalRegistry

  static Registry getRegistry() {
    return globalRegistry
  }

  @Autowired
  Registry autowiredRegistry

  @PostConstruct
  public void bindGlobalRegistry() {
    globalRegistry = autowiredRegistry
  }

  final static String TAG_BATCH_CONTEXT = "context"
  final static String TAG_REGION = "region"
  final static String TAG_SCOPE = "scope"
  final static String TAG_ZONE = "zone"
  final static String SCOPE_GLOBAL = "global"
  final static String SCOPE_REGIONAL = "regional"
  final static String SCOPE_ZONAL = "zonal"

  public static  T timeExecuteBatch(Registry spectator_registry, GoogleBatchRequest batch, String batchContext, String... tags) throws IOException {
     def batchSize = batch.size()
     def success = "false"
     Clock clock = spectator_registry.clock()
     long startTime = clock.monotonicTime()
     int statusCode = 200

     try {
       batch.execute()
       success = "true"
     } catch (HttpResponseException e) {
       statusCode = e.getStatusCode()
     } finally {
       def status = statusCode.toString()[0] + "xx"

       def tagDetails = [(TAG_BATCH_CONTEXT): batchContext, "success": success, "status": status, "statusCode": statusCode.toString()]
       long nanos = clock.monotonicTime() - startTime
       spectator_registry.timer(spectator_registry.createId("google.batchExecute", tags).withTags(tagDetails)).record(nanos, TimeUnit.NANOSECONDS)
       spectator_registry.counter(spectator_registry.createId("google.batchSize", tags).withTags(tagDetails)).increment(batchSize)
     }
  }

  public static  T timeExecute(Registry spectator_registry, AbstractGoogleClientRequest request, String metric_name, String api, String... tags) throws IOException {
     def success = "false"
     T result
     Clock clock = spectator_registry.clock()
     long startTime = clock.monotonicTime()
     int statusCode = -1

     try {
       result = request.execute()
       success = "true"
       statusCode = request.getLastStatusCode()
     } catch (HttpResponseException e) {
       statusCode = e.getStatusCode()
       throw e
     } finally {
       long nanos = clock.monotonicTime() - startTime
       def status = statusCode.toString()[0] + "xx"

       def tagDetails = ["api": api, "success": success, "status": status, "statusCode": statusCode.toString() ]
       spectator_registry.timer(spectator_registry.createId(metric_name, tags).withTags(tagDetails)).record(nanos, TimeUnit.NANOSECONDS)
     }
     return result
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy