# Copyright 2017 Google Inc. All rights reserved. # # 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. ; require "shellwords" module AppEngine module Util ## # A collection of utility functions and classes for interacting with an # installation of the gcloud SDK. # module Gcloud ## # Base class for gcloud related errors. # class Error < ::StandardError end ## # Exception raised when the gcloud binary could not be found. # class BinaryNotFound < Gcloud::Error def initialize super "GCloud binary not found in path" end end ## # Exception raised when the project gcloud config is not set. # class ProjectNotSet < Gcloud::Error def initialize super "GCloud project configuration not set" end end ## # Exception raised when gcloud auth has not been completed. # class GcloudNotAuthenticated < Gcloud::Error def initialize super "GCloud not authenticated" end end ## # Exception raised when gcloud fails and returns an error. # class GcloudFailed < Gcloud::Error def initialize code super "GCloud failed with result code #{code}" end end class << self ## # @private # Returns the path to the gcloud binary, or nil if the binary could # not be found. # # @return [String,nil] Path to the gcloud binary. # def binary_path unless defined? @binary_path if Gem.win_platform? @binary_path = `where gcloud` == '' ? nil : 'gcloud' else @binary_path = `which gcloud`.strip @binary_path = nil if @binary_path.empty? end end @binary_path end ## # @private # Returns the path to the gcloud binary. Raises BinaryNotFound if the # binary could not be found. # # @return [String] Path to the gcloud binary. # @raise [BinaryNotFound] The gcloud binary is not present. # def binary_path! value = binary_path raise BinaryNotFound unless value value end ## # @private # Returns the ID of the current project, or nil if no project has # been set. # # @return [String,nil] ID of the current project. # def current_project unless defined? @current_project @current_project = execute [ "config", "list", "core/project", "--format=value(core.project)" ], capture: true @current_project = nil if @current_project.empty? end @current_project end ## # @private # Returns the ID of the current project. Raises ProjectNotSet if no # project has been set in the gcloud configuration. # # @return [String] ID of the current project. # @raise [ProjectNotSet] The project config has not been set. # def current_project! value = current_project raise ProjectNotSet if value.empty? value end ## # @private # Verifies that all gcloud related dependencies are satisfied. # Specifically, verifies that the gcloud binary is installed and # authenticated, and a project has been set. # # @raise [BinaryNotFound] The gcloud binary is not present. # @raise [ProjectNotSet] The project config has not been set. # @raise [GcloudNotAuthenticated] Gcloud has not been authenticated. # def verify! binary_path! current_project! auths = execute ["auth", "list", "--format=value(account)"], capture: true raise GcloudNotAuthenticated if auths.empty? end ## # @private # Execute a given gcloud command in a subshell. # # @param args [Array] The gcloud args. # @param echo [boolean] Whether to echo the command to the terminal. # Defaults to false. # @param capture [boolean] If true, return the output. If false, return # a boolean indicating success or failure. Defaults to false. # @param assert [boolean] If true, raise GcloudFailed on failure. # Defaults to true. # @return [String,Integer] Either the output or the success status, # depending on the value of the `capture` parameter. # def execute args, echo: false, capture: false, assert: true joined_args = Gem.win_platform? ? args.join(" ") : Shellwords.join(args) cmd = "#{binary_path!} #{joined_args}" puts cmd if echo result = capture ? `#{cmd}` : system(cmd) code = $?.exitstatus raise GcloudFailed.new($?.exitstatus) if assert && code != 0 result end ## # @private # Execute a given gcloud command in a subshell, and return the output # as a string. # # @param args [Array] The gcloud args. # @return [String] The command output. # def capture args execute args, capture: true end end end end end