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 }