github.com/coreos/mantle@v0.13.0/platform/api/azure/instance.go (about)

     1  // Copyright 2018 CoreOS, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package azure
    16  
    17  import (
    18  	"encoding/base64"
    19  	"fmt"
    20  	"io"
    21  	"io/ioutil"
    22  	"regexp"
    23  	"time"
    24  
    25  	"github.com/Azure/azure-sdk-for-go/arm/compute"
    26  	"github.com/Azure/azure-sdk-for-go/arm/network"
    27  
    28  	"github.com/coreos/mantle/util"
    29  )
    30  
    31  type Machine struct {
    32  	ID               string
    33  	PublicIPAddress  string
    34  	PrivateIPAddress string
    35  	InterfaceName    string
    36  	PublicIPName     string
    37  }
    38  
    39  func (a *API) getVMParameters(name, userdata, sshkey, storageAccountURI string, ip *network.PublicIPAddress, nic *network.Interface) compute.VirtualMachine {
    40  	osProfile := compute.OSProfile{
    41  		AdminUsername: util.StrToPtr("core"),
    42  		ComputerName:  &name,
    43  		LinuxConfiguration: &compute.LinuxConfiguration{
    44  			SSH: &compute.SSHConfiguration{
    45  				PublicKeys: &[]compute.SSHPublicKey{
    46  					{
    47  						Path:    util.StrToPtr("/home/core/.ssh/authorized_keys"),
    48  						KeyData: &sshkey,
    49  					},
    50  				},
    51  			},
    52  		},
    53  	}
    54  	if userdata != "" {
    55  		ud := base64.StdEncoding.EncodeToString([]byte(userdata))
    56  		osProfile.CustomData = &ud
    57  	}
    58  	var imgRef *compute.ImageReference
    59  	if a.opts.DiskURI != "" {
    60  		imgRef = &compute.ImageReference{
    61  			ID: &a.opts.DiskURI,
    62  		}
    63  	} else {
    64  		imgRef = &compute.ImageReference{
    65  			Publisher: &a.opts.Publisher,
    66  			Offer:     &a.opts.Offer,
    67  			Sku:       &a.opts.Sku,
    68  			Version:   &a.opts.Version,
    69  		}
    70  	}
    71  	return compute.VirtualMachine{
    72  		Name:     &name,
    73  		Location: &a.opts.Location,
    74  		Tags: &map[string]*string{
    75  			"createdBy": util.StrToPtr("mantle"),
    76  		},
    77  		VirtualMachineProperties: &compute.VirtualMachineProperties{
    78  			HardwareProfile: &compute.HardwareProfile{
    79  				VMSize: compute.VirtualMachineSizeTypes(a.opts.Size),
    80  			},
    81  			StorageProfile: &compute.StorageProfile{
    82  				ImageReference: imgRef,
    83  				OsDisk: &compute.OSDisk{
    84  					CreateOption: compute.FromImage,
    85  				},
    86  			},
    87  			OsProfile: &osProfile,
    88  			NetworkProfile: &compute.NetworkProfile{
    89  				NetworkInterfaces: &[]compute.NetworkInterfaceReference{
    90  					{
    91  						ID: nic.ID,
    92  						NetworkInterfaceReferenceProperties: &compute.NetworkInterfaceReferenceProperties{
    93  							Primary: util.BoolToPtr(true),
    94  						},
    95  					},
    96  				},
    97  			},
    98  			DiagnosticsProfile: &compute.DiagnosticsProfile{
    99  				BootDiagnostics: &compute.BootDiagnostics{
   100  					Enabled:    util.BoolToPtr(true),
   101  					StorageURI: &storageAccountURI,
   102  				},
   103  			},
   104  		},
   105  	}
   106  }
   107  
   108  func (a *API) CreateInstance(name, userdata, sshkey, resourceGroup, storageAccount string) (*Machine, error) {
   109  	subnet, err := a.getSubnet(resourceGroup)
   110  	if err != nil {
   111  		return nil, fmt.Errorf("preparing network resources: %v", err)
   112  	}
   113  
   114  	ip, err := a.createPublicIP(resourceGroup)
   115  	if err != nil {
   116  		return nil, fmt.Errorf("creating public ip: %v", err)
   117  	}
   118  	if ip.Name == nil {
   119  		return nil, fmt.Errorf("couldn't get public IP name")
   120  	}
   121  
   122  	nic, err := a.createNIC(ip, &subnet, resourceGroup)
   123  	if err != nil {
   124  		return nil, fmt.Errorf("creating nic: %v", err)
   125  	}
   126  	if nic.Name == nil {
   127  		return nil, fmt.Errorf("couldn't get NIC name")
   128  	}
   129  
   130  	vmParams := a.getVMParameters(name, userdata, sshkey, fmt.Sprintf("https://%s.blob.core.windows.net/", storageAccount), ip, nic)
   131  
   132  	_, err = a.compClient.CreateOrUpdate(resourceGroup, name, vmParams, nil)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	err = util.WaitUntilReady(5*time.Minute, 10*time.Second, func() (bool, error) {
   138  		vm, err := a.compClient.Get(resourceGroup, name, "")
   139  		if err != nil {
   140  			return false, err
   141  		}
   142  
   143  		if vm.VirtualMachineProperties.ProvisioningState != nil && *vm.VirtualMachineProperties.ProvisioningState != "Succeeded" {
   144  			return false, nil
   145  		}
   146  
   147  		return true, nil
   148  	})
   149  	if err != nil {
   150  		a.TerminateInstance(name, resourceGroup)
   151  		return nil, fmt.Errorf("waiting for machine to become active: %v", err)
   152  	}
   153  
   154  	vm, err := a.compClient.Get(resourceGroup, name, "")
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  
   159  	if vm.Name == nil {
   160  		return nil, fmt.Errorf("couldn't get VM ID")
   161  	}
   162  
   163  	publicaddr, privaddr, err := a.GetIPAddresses(*nic.Name, *ip.Name, resourceGroup)
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  
   168  	return &Machine{
   169  		ID:               *vm.Name,
   170  		PublicIPAddress:  publicaddr,
   171  		PrivateIPAddress: privaddr,
   172  		InterfaceName:    *nic.Name,
   173  		PublicIPName:     *ip.Name,
   174  	}, nil
   175  }
   176  
   177  func (a *API) TerminateInstance(name, resourceGroup string) error {
   178  	_, err := a.compClient.Delete(resourceGroup, name, nil)
   179  	return err
   180  }
   181  
   182  func (a *API) GetConsoleOutput(name, resourceGroup, storageAccount string) ([]byte, error) {
   183  	kr, err := a.GetStorageServiceKeysARM(storageAccount, resourceGroup)
   184  	if err != nil {
   185  		return nil, fmt.Errorf("retrieving storage service keys: %v", err)
   186  	}
   187  
   188  	if kr.Keys == nil {
   189  		return nil, fmt.Errorf("no storage service keys found")
   190  	}
   191  	k := *kr.Keys
   192  	key := *k[0].Value
   193  
   194  	vm, err := a.compClient.Get(resourceGroup, name, compute.InstanceView)
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  
   199  	consoleURI := vm.VirtualMachineProperties.InstanceView.BootDiagnostics.SerialConsoleLogBlobURI
   200  	if consoleURI == nil {
   201  		return nil, fmt.Errorf("serial console URI is nil")
   202  	}
   203  
   204  	// Only the full URI to the logs are present in the virtual machine
   205  	// properties. Parse out the container & file name to use the GetBlob
   206  	// API call directly.
   207  	uri := []byte(*consoleURI)
   208  	containerPat := regexp.MustCompile(`bootdiagnostics-kola[a-z0-9\-]+`)
   209  	container := string(containerPat.Find(uri))
   210  	namePat := regexp.MustCompile(`kola-[a-z0-9\-\.]+.serialconsole.log`)
   211  	blobname := string(namePat.Find(uri))
   212  
   213  	var data io.ReadCloser
   214  	err = util.Retry(6, 10*time.Second, func() error {
   215  		data, err = a.GetBlob(storageAccount, key, container, blobname)
   216  		return err
   217  	})
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  
   222  	return ioutil.ReadAll(data)
   223  }