github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/configstate/configcore/cloud.go (about)

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