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  }