• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright 2018 - The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16"""google SDK tools installer.
17
18This class will return the path to the google sdk tools and download them into
19a temporary dir if they don't exist. The caller is expected to call cleanup
20when they're done.
21
22Example usage:
23google_sdk = GoogleSDK()
24google_sdk_bin_path = google_sdk.GetSDKBinPath()
25
26# Caller is done.
27google_sdk.CleanUp()
28"""
29
30from distutils.spawn import find_executable
31import logging
32import os
33import platform
34import shutil
35import sys
36import tempfile
37import urllib2
38
39from acloud import errors
40from acloud.internal.lib import utils
41
42SDK_BIN_PATH = os.path.join("google-cloud-sdk", "bin")
43GCLOUD_BIN = "gcloud"
44GCP_SDK_VERSION = "209.0.0"
45GCP_SDK_TOOLS_URL = "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads"
46LINUX_GCP_SDK_64_URL = "%s/google-cloud-sdk-%s-linux-x86_64.tar.gz" % (
47    GCP_SDK_TOOLS_URL, GCP_SDK_VERSION)
48LINUX_GCP_SDK_32_URL = "%s/google-cloud-sdk-%s-linux-x86.tar.gz" % (
49    GCP_SDK_TOOLS_URL, GCP_SDK_VERSION)
50WIN_GCP_SDK_64_URL = "%s/google-cloud-sdk-%s-windows-x86_64.zip" % (
51    GCP_SDK_TOOLS_URL, GCP_SDK_VERSION)
52WIN_GCP_SDK_32_URL = "%s/google-cloud-sdk-%s-windows-x86.zip" % (
53    GCP_SDK_TOOLS_URL, GCP_SDK_VERSION)
54MAC_GCP_SDK_64_URL = "%s/google-cloud-sdk-%s-darwin-x86_64.tar.gz" % (
55    GCP_SDK_TOOLS_URL, GCP_SDK_VERSION)
56MAC_GCP_SDK_32_URL = "%s/google-cloud-sdk-%s-darwin-x86.tar.gz" % (
57    GCP_SDK_TOOLS_URL, GCP_SDK_VERSION)
58LINUX = "linux"
59WIN = "windows"
60MAC = "darwin"
61
62logger = logging.getLogger(__name__)
63
64
65def GetSdkUrl():
66    """Get google SDK tool url.
67
68    Return:
69        String, a URL of google SDK tools.
70
71    Raises:
72        OSTypeError when OS type is neither linux, mac, or windows.
73    """
74    is_64bits = sys.maxsize > 2**32
75    os_type = platform.system().lower()
76    if is_64bits:
77        if os_type == LINUX:
78            return LINUX_GCP_SDK_64_URL
79        elif os_type == MAC:
80            return MAC_GCP_SDK_64_URL
81        elif os_type == WIN:
82            return WIN_GCP_SDK_64_URL
83    else:
84        if os_type == LINUX:
85            return LINUX_GCP_SDK_32_URL
86        elif os_type == MAC:
87            return MAC_GCP_SDK_32_URL
88        elif os_type == WIN:
89            return WIN_GCP_SDK_32_URL
90    raise errors.OSTypeError("no gcloud for os type: %s" % (os_type))
91
92
93def SDKInstalled():
94    """Check google SDK tools installed.
95
96    We'll try to find where gcloud is and assume the other google sdk tools
97    live in the same location.
98
99    Return:
100        Boolean, return True if gcloud is installed, False otherwise.
101    """
102    if find_executable(GCLOUD_BIN):
103        return True
104    return False
105
106
107class GoogleSDK(object):
108    """Google SDK tools installer."""
109
110    def __init__(self):
111        """GoogleSDKInstaller initialize.
112
113        Make sure the GCloud SDK is installed. If not, this function will assist
114        users to install.
115        """
116        self._tmp_path = None
117        self._tmp_sdk_path = None
118        if not SDKInstalled():
119            self.DownloadGcloudSDKAndExtract()
120
121    def GetSDKBinPath(self):
122        """Get google SDK tools bin path.
123
124        We assumed there are google sdk tools somewhere and will raise if we
125        can't find it. The order we're looking for is:
126        1. Builtin gcloud (usually /usr/bin), we'll return /usr/bin with the
127           assumption other sdk tools live there.
128        2. Downloaded google sdk (self._tmp_dir), we assumed the caller already
129           downloaded/extracted and set the self._tmp_sdk_path for us to return.
130           We'll make sure it exists prior to returning it.
131
132        Return:
133            String, return google SDK tools bin path.
134
135        Raise:
136            NoGoogleSDKDetected if we can't find the sdk path.
137        """
138        builtin_gcloud = find_executable(GCLOUD_BIN)
139        if builtin_gcloud:
140            return os.path.dirname(builtin_gcloud)
141        elif os.path.exists(self._tmp_sdk_path):
142            return self._tmp_sdk_path
143        raise errors.NoGoogleSDKDetected("no sdk path.")
144
145    def DownloadGcloudSDKAndExtract(self):
146        """Download the google SDK tools and decompress it.
147
148        Download the google SDK from the GCP web.
149        Reference https://cloud.google.com/sdk/docs/downloads-versioned-archives.
150        """
151        self._tmp_path = tempfile.mkdtemp(prefix="gcloud")
152        url = GetSdkUrl()
153        filename = url[url.rfind("/") + 1:]
154        file_path = os.path.join(self._tmp_path, filename)
155        logger.info("Download file from: %s", url)
156        logger.info("Save the file to: %s", file_path)
157        url_stream = urllib2.urlopen(url)
158        metadata = url_stream.info()
159        file_size = int(metadata.getheaders("Content-Length")[0])
160        logger.info("Downloading google SDK: %s bytes.", file_size)
161        with open(file_path, 'wb') as output:
162            output.write(url_stream.read())
163        utils.Decompress(file_path, self._tmp_path)
164        self._tmp_sdk_path = os.path.join(self._tmp_path, SDK_BIN_PATH)
165
166    def CleanUp(self):
167        """Clean google sdk tools install folder."""
168        if self._tmp_path and os.path.exists(self._tmp_path):
169            shutil.rmtree(self._tmp_path)
170