github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/overlord/configstate/configcore/cloud.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 // +build !nomanagers 3 4 /* 5 * Copyright (C) 2018 Canonical Ltd 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 3 as 9 * published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 package configcore 22 23 import ( 24 "encoding/json" 25 "io/ioutil" 26 "os" 27 28 "github.com/snapcore/snapd/dirs" 29 "github.com/snapcore/snapd/logger" 30 "github.com/snapcore/snapd/overlord/auth" 31 "github.com/snapcore/snapd/overlord/configstate/config" 32 "github.com/snapcore/snapd/overlord/state" 33 ) 34 35 func alreadySeeded(tr config.Conf) (bool, error) { 36 st := tr.State() 37 st.Lock() 38 defer st.Unlock() 39 var seeded bool 40 err := tr.State().Get("seeded", &seeded) 41 if err != nil && err != state.ErrNoState { 42 return false, err 43 } 44 return seeded, nil 45 } 46 47 type cloudInitInstanceData struct { 48 V1 struct { 49 Region string 50 Name string 51 AvailabilityZone string 52 } 53 } 54 55 func (c *cloudInitInstanceData) UnmarshalJSON(bs []byte) error { 56 var instanceDataJSON struct { 57 V1 struct { 58 Region string `json:"region"` 59 // these fields can come with - or _ as separators 60 Name string `json:"cloud_name"` 61 AltName string `json:"cloud-name"` 62 AvailabilityZone string `json:"availability_zone"` 63 AltAvailabilityZone string `json:"availability-zone"` 64 } `json:"v1"` 65 } 66 67 if err := json.Unmarshal(bs, &instanceDataJSON); err != nil { 68 return err 69 } 70 71 c.V1.Region = instanceDataJSON.V1.Region 72 switch { 73 case instanceDataJSON.V1.Name != "": 74 c.V1.Name = instanceDataJSON.V1.Name 75 c.V1.AvailabilityZone = instanceDataJSON.V1.AvailabilityZone 76 case instanceDataJSON.V1.AltName != "": 77 c.V1.Name = instanceDataJSON.V1.AltName 78 c.V1.AvailabilityZone = instanceDataJSON.V1.AltAvailabilityZone 79 } 80 return nil 81 } 82 83 func setCloudInfoWhenSeeding(tr config.Conf, opts *fsOnlyContext) error { 84 // if we are during seeding try to capture cloud information 85 seeded, err := alreadySeeded(tr) 86 if err != nil { 87 return err 88 } 89 if seeded { 90 // already done 91 return nil 92 } 93 94 data, err := ioutil.ReadFile(dirs.CloudInstanceDataFile) 95 if os.IsNotExist(err) { 96 // nothing to do 97 return nil 98 } 99 if err != nil { 100 logger.Noticef("cannot read cloud instance information %q: %v", dirs.CloudInstanceDataFile, err) 101 return nil 102 } 103 104 var instanceData cloudInitInstanceData 105 err = json.Unmarshal(data, &instanceData) 106 if err != nil { 107 logger.Noticef("cannot unmarshal cloud instance information %q: %v", dirs.CloudInstanceDataFile, err) 108 return nil 109 } 110 111 cloudName := instanceData.V1.Name 112 if cloudName == "" || cloudName == "nocloud" || cloudName == "none" { 113 // not a cloud 114 return nil 115 } 116 117 tr.Set("core", "cloud", auth.CloudInfo{ 118 Name: cloudName, 119 Region: instanceData.V1.Region, 120 AvailabilityZone: instanceData.V1.AvailabilityZone, 121 }) 122 return nil 123 }