github.com/jenkins-x/jx/v2@v2.1.155/pkg/cloud/aks/aks.go (about)

     1  package aks
     2  
     3  import (
     4  	b64 "encoding/base64"
     5  	"encoding/json"
     6  	"strings"
     7  
     8  	"github.com/jenkins-x/jx-logging/pkg/log"
     9  	"github.com/jenkins-x/jx/v2/pkg/util"
    10  )
    11  
    12  // AzureRunner an Azure CLI runner to interact with Azure
    13  type AzureRunner struct {
    14  	Runner util.Commander
    15  }
    16  
    17  type aks struct {
    18  	ID    string `json:"id"`
    19  	URI   string `json:"uri"`
    20  	Group string `json:"group"`
    21  	Name  string `json:"name"`
    22  }
    23  
    24  type acr struct {
    25  	ID    string `json:"id"`
    26  	URI   string `json:"uri"`
    27  	Group string `json:"group"`
    28  	Name  string `json:"name"`
    29  }
    30  
    31  type password struct {
    32  	Name  string `json:"name"`
    33  	Value string `json:"value"`
    34  }
    35  
    36  type credential struct {
    37  	Passwords []password `json:"passwords"`
    38  	Username  string     `json:"username"`
    39  }
    40  
    41  type auth struct {
    42  	Auth string `json:"auth,omitempty"`
    43  }
    44  
    45  type config struct {
    46  	Auths map[string]*auth `json:"auths,omitempty"`
    47  }
    48  
    49  // NewAzureRunnerWithCommander specific the command runner for Azure CLI.
    50  func NewAzureRunnerWithCommander(runner util.Commander) *AzureRunner {
    51  	return &AzureRunner{
    52  		Runner: runner,
    53  	}
    54  }
    55  
    56  // NewAzureRunner return a new AzureRunner
    57  func NewAzureRunner() *AzureRunner {
    58  	runner := &util.Command{}
    59  	return NewAzureRunnerWithCommander(runner)
    60  }
    61  
    62  // GetClusterClient return AKS resource group, name and client ID.
    63  func (az *AzureRunner) GetClusterClient(server string) (string, string, string, error) {
    64  	clientID := ""
    65  	group := ""
    66  	name := ""
    67  
    68  	clusterstr, err := az.azureCLI("aks", "list", "--query", "[].{uri:fqdn,id:servicePrincipalProfile.clientId,group:resourceGroup,name:name}")
    69  	if err != nil {
    70  		return group, name, clientID, err
    71  	}
    72  
    73  	clusters := []aks{}
    74  	err = json.Unmarshal([]byte(clusterstr), &clusters)
    75  	if err != nil {
    76  		return group, name, clientID, err
    77  	}
    78  
    79  	for _, v := range clusters {
    80  		if "https://"+v.URI+":443" == server {
    81  			clientID = v.ID
    82  			name = v.Name
    83  			group = v.Group
    84  			break
    85  		}
    86  	}
    87  
    88  	return group, name, clientID, err
    89  }
    90  
    91  // GetRegistry Return the docker registry config, registry login server and resource id, error
    92  func (az *AzureRunner) GetRegistry(azureRegistrySubscription string, resourceGroup string, name string, registry string) (string, string, string, error) {
    93  	registryID := ""
    94  	loginServer := registry
    95  	dockerConfig := ""
    96  
    97  	if registry == "" {
    98  		loginServer = name + ".azurecr.io"
    99  	}
   100  
   101  	if !strings.HasSuffix(loginServer, "azurecr.io") {
   102  		return dockerConfig, loginServer, registryID, nil
   103  	}
   104  
   105  	acrRG, acrName, registryID, err := az.getRegistryID(azureRegistrySubscription, loginServer)
   106  	if err != nil {
   107  		return dockerConfig, loginServer, registryID, err
   108  	}
   109  	// not exist and create a new one in resourceGroup
   110  	if registryID == "" {
   111  		acrRG = resourceGroup
   112  		acrName = name
   113  		registryID, loginServer, err = az.createRegistry(azureRegistrySubscription, acrRG, acrName)
   114  		if err != nil {
   115  			return dockerConfig, loginServer, registryID, err
   116  		}
   117  	}
   118  	dockerConfig, err = az.getACRCredential(azureRegistrySubscription, acrRG, acrName)
   119  	return dockerConfig, loginServer, registryID, err
   120  }
   121  
   122  // AssignRole Assign the client a reader role for registry.
   123  func (az *AzureRunner) AssignRole(client string, registry string) {
   124  	if client == "" || registry == "" {
   125  		return
   126  	}
   127  	az.azureCLI("role", "assignment", "create", "--assignee", client, "--role", "Reader", "--scope", registry) //nolint:errcheck
   128  }
   129  
   130  // getRegistryID returns acrRG, acrName, acrID, error
   131  func (az *AzureRunner) getRegistryID(azureRegistrySubscription string, loginServer string) (string, string, string, error) {
   132  	acrRG := ""
   133  	acrName := ""
   134  	acrID := ""
   135  
   136  	acrListArgs := []string{
   137  		"acr",
   138  		"list",
   139  		"--query",
   140  		"[].{uri:loginServer,id:id,name:name,group:resourceGroup}",
   141  	}
   142  
   143  	if azureRegistrySubscription != "" {
   144  		acrListArgs = append(acrListArgs, "--subscription", azureRegistrySubscription)
   145  	}
   146  
   147  	acrList, err := az.azureCLI(acrListArgs...)
   148  
   149  	if err != nil {
   150  		log.Logger().Infof("Registry %s is not exist", util.ColorInfo(loginServer))
   151  	} else {
   152  		registries := []acr{}
   153  		err = json.Unmarshal([]byte(acrList), &registries)
   154  		if err != nil {
   155  			return "", "", "", err
   156  		}
   157  		for _, v := range registries {
   158  			if v.URI == loginServer {
   159  				acrID = v.ID
   160  				acrRG = v.Group
   161  				acrName = v.Name
   162  				break
   163  			}
   164  		}
   165  	}
   166  	return acrRG, acrName, acrID, nil
   167  }
   168  
   169  // createRegistry return resource ID, login server and error
   170  func (az *AzureRunner) createRegistry(azureRegistrySubscription string, resourceGroup string, name string) (string, string, error) {
   171  	acrCreateArgs := []string{
   172  		"acr",
   173  		"create",
   174  		"-g",
   175  		resourceGroup,
   176  		"-n",
   177  		name,
   178  		"--sku",
   179  		"Standard",
   180  		"--admin-enabled",
   181  		"--query",
   182  		"id",
   183  		"-o",
   184  		"tsv",
   185  	}
   186  
   187  	if azureRegistrySubscription != "" {
   188  		acrCreateArgs = append(acrCreateArgs, "--subscription", azureRegistrySubscription)
   189  	}
   190  
   191  	registryID, err := az.azureCLI(acrCreateArgs...)
   192  	if err != nil {
   193  		log.Logger().Infof("Failed to create ACR %s in resource group %s", util.ColorInfo(name), util.ColorInfo(resourceGroup))
   194  		return "", "", err
   195  	}
   196  	return registryID, formatLoginServer(name), nil
   197  }
   198  
   199  // getACRCredential return .dockerconfig value for the ACR
   200  func (az *AzureRunner) getACRCredential(azureRegistrySubscription string, resourceGroup string, name string) (string, error) {
   201  	showCredArgs := []string{
   202  		"acr",
   203  		"credential",
   204  		"show",
   205  		"-g",
   206  		resourceGroup,
   207  		"-n",
   208  		name,
   209  	}
   210  
   211  	if azureRegistrySubscription != "" {
   212  		showCredArgs = append(showCredArgs, "--subscription", azureRegistrySubscription)
   213  	}
   214  
   215  	credstr, err := az.azureCLI(showCredArgs...)
   216  	if err != nil {
   217  		log.Logger().Infof("Failed to get credential for ACR %s in resource group %s", util.ColorInfo(name), util.ColorInfo(resourceGroup))
   218  		return "", err
   219  	}
   220  	cred := credential{}
   221  	err = json.Unmarshal([]byte(credstr), &cred)
   222  	if err != nil {
   223  		return "", err
   224  	}
   225  	newSecret := &auth{}
   226  	dockerConfig := &config{}
   227  	newSecret.Auth = b64.StdEncoding.EncodeToString([]byte(cred.Username + ":" + cred.Passwords[0].Value))
   228  	if dockerConfig.Auths == nil {
   229  		dockerConfig.Auths = map[string]*auth{}
   230  	}
   231  	dockerConfig.Auths[formatLoginServer(name)] = newSecret
   232  	dockerConfigStr, err := json.Marshal(dockerConfig)
   233  	return string(dockerConfigStr), err
   234  }
   235  
   236  func formatLoginServer(name string) string {
   237  	return name + ".azurecr.io"
   238  }
   239  
   240  func (az *AzureRunner) azureCLI(args ...string) (string, error) {
   241  	az.Runner.SetName("az")
   242  	az.Runner.SetArgs(args)
   243  	return az.Runner.RunWithoutRetry()
   244  }