1# -------------------------------------------------------------------------------------------- 2# Copyright (c) Microsoft Corporation. All rights reserved. 3# Licensed under the MIT License. See License.txt in the project root for license information. 4# -------------------------------------------------------------------------------------------- 5 6from knack.util import CLIError 7from knack.log import get_logger 8 9from ._utils import user_confirmation 10from ._docker_utils import get_access_credentials, request_data_from_registry, RegistryException 11 12 13logger = get_logger(__name__) 14 15 16def acr_helm_list(cmd, 17 registry_name, 18 repository='repo', 19 resource_group_name=None, # pylint: disable=unused-argument 20 tenant_suffix=None, 21 username=None, 22 password=None): 23 login_server, username, password = get_access_credentials( 24 cmd=cmd, 25 registry_name=registry_name, 26 tenant_suffix=tenant_suffix, 27 username=username, 28 password=password, 29 artifact_repository=repository, 30 permission='pull') 31 32 return request_data_from_registry( 33 http_method='get', 34 login_server=login_server, 35 path=_get_charts_path(repository), 36 username=username, 37 password=password)[0] 38 39 40def acr_helm_show(cmd, 41 registry_name, 42 chart, 43 version=None, 44 repository='repo', 45 resource_group_name=None, # pylint: disable=unused-argument 46 tenant_suffix=None, 47 username=None, 48 password=None): 49 login_server, username, password = get_access_credentials( 50 cmd=cmd, 51 registry_name=registry_name, 52 tenant_suffix=tenant_suffix, 53 username=username, 54 password=password, 55 artifact_repository=repository, 56 permission='pull') 57 58 return request_data_from_registry( 59 http_method='get', 60 login_server=login_server, 61 path=_get_charts_path(repository, chart, version), 62 username=username, 63 password=password)[0] 64 65 66def acr_helm_delete(cmd, 67 registry_name, 68 chart, 69 version=None, 70 repository='repo', 71 resource_group_name=None, # pylint: disable=unused-argument 72 tenant_suffix=None, 73 username=None, 74 password=None, 75 prov=False, 76 yes=False): 77 if version: 78 message = "This operation will delete the chart package '{}'".format( 79 _get_chart_package_name(chart, version, prov)) 80 else: 81 message = "This operation will delete all versions of the chart '{}'".format(chart) 82 user_confirmation("{}.\nAre you sure you want to continue?".format(message), yes) 83 84 login_server, username, password = get_access_credentials( 85 cmd=cmd, 86 registry_name=registry_name, 87 tenant_suffix=tenant_suffix, 88 username=username, 89 password=password, 90 artifact_repository=repository, 91 permission='delete') 92 93 return request_data_from_registry( 94 http_method='delete', 95 login_server=login_server, 96 path=_get_blobs_path(repository, chart, version, prov) if version else _get_charts_path(repository, chart), 97 username=username, 98 password=password)[0] 99 100 101def acr_helm_push(cmd, 102 registry_name, 103 chart_package, 104 repository='repo', 105 force=False, 106 resource_group_name=None, # pylint: disable=unused-argument 107 tenant_suffix=None, 108 username=None, 109 password=None): 110 from os.path import isdir, basename 111 112 if isdir(chart_package): 113 raise CLIError("Please run 'helm package {}' to generate a chart package first.".format(chart_package)) 114 115 login_server, username, password = get_access_credentials( 116 cmd=cmd, 117 registry_name=registry_name, 118 tenant_suffix=tenant_suffix, 119 username=username, 120 password=password, 121 artifact_repository=repository, 122 permission='push,pull') 123 124 path = _get_blobs_path(repository, basename(chart_package)) 125 126 try: 127 result = request_data_from_registry( 128 http_method='patch' if force else 'put', 129 login_server=login_server, 130 path=path, 131 username=username, 132 password=password, 133 file_payload=chart_package)[0] 134 return result 135 except RegistryException as e: 136 # Fallback using PUT if the chart doesn't exist 137 if e.status_code == 404 and force: 138 return request_data_from_registry( 139 http_method='put', 140 login_server=login_server, 141 path=path, 142 username=username, 143 password=password, 144 file_payload=chart_package)[0] 145 raise 146 147 148def acr_helm_repo_add(cmd, 149 registry_name, 150 repository='repo', 151 resource_group_name=None, # pylint: disable=unused-argument 152 tenant_suffix=None, 153 username=None, 154 password=None): 155 helm_command, _ = get_helm_command() 156 157 login_server, username, password = get_access_credentials( 158 cmd=cmd, 159 registry_name=registry_name, 160 tenant_suffix=tenant_suffix, 161 username=username, 162 password=password, 163 artifact_repository=repository, 164 permission='pull') 165 166 from subprocess import Popen 167 p = Popen([helm_command, 'repo', 'add', registry_name, 168 'https://{}/helm/v1/{}'.format(login_server, repository), 169 '--username', username, '--password', password]) 170 p.wait() 171 172 173def get_helm_command(is_diagnostics_context=False): 174 from ._errors import HELM_COMMAND_ERROR 175 helm_command = 'helm' 176 177 from subprocess import PIPE, Popen 178 try: 179 p = Popen([helm_command, "--help"], stdout=PIPE, stderr=PIPE) 180 _, stderr = p.communicate() 181 except OSError as e: 182 logger.debug("Could not run '%s' command. Exception: %s", helm_command, str(e)) 183 # The executable may not be discoverable in WSL so retry *.exe once 184 try: 185 helm_command = 'helm.exe' 186 p = Popen([helm_command, "--help"], stdout=PIPE, stderr=PIPE) 187 _, stderr = p.communicate() 188 except OSError as inner: 189 logger.debug("Could not run '%s' command. Exception: %s", helm_command, str(inner)) 190 if is_diagnostics_context: 191 return None, HELM_COMMAND_ERROR 192 raise CLIError(HELM_COMMAND_ERROR.get_error_message()) 193 194 if stderr: 195 if is_diagnostics_context: 196 return None, HELM_COMMAND_ERROR.append_error_message(stderr.decode()) 197 raise CLIError(HELM_COMMAND_ERROR.append_error_message(stderr.decode()).get_error_message()) 198 199 return helm_command, None 200 201 202def _get_charts_path(repository, chart=None, version=None): 203 if chart and version: 204 return '/helm/v1/{}/_charts/{}/{}'.format(repository, chart, version) 205 206 if chart: 207 return '/helm/v1/{}/_charts/{}'.format(repository, chart) 208 209 return '/helm/v1/{}/_charts'.format(repository) 210 211 212def _get_blobs_path(repository, chart, version=None, prov=False): 213 path = '/helm/v1/{}/_blobs'.format(repository) 214 215 if version: 216 return '{}/{}'.format(path, _get_chart_package_name(chart, version, prov)) 217 218 return '{}/{}'.format(path, chart) 219 220 221def _get_chart_package_name(chart, version, prov=False): 222 chart_package_name = '{}-{}.tgz'.format(chart, version) 223 224 if prov: 225 return '{}.prov'.format(chart_package_name) 226 227 return chart_package_name 228