mirror of
https://github.com/siderolabs/talos.git
synced 2025-08-24 16:11:11 +02:00
Instead of passing around a struct, it is better if we pass around an interface that describes the behavior we want. The Runtime interface provides a common place to describe runtime specific parameters. This initial implementation offers the runtime mode, the platform specifics, and the config. Signed-off-by: Andrew Rynhard <andrew@andrewrynhard.com>
228 lines
6.2 KiB
Go
228 lines
6.2 KiB
Go
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
package azure
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/xml"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
)
|
|
|
|
// This should provide the bare minimum to trigger a node in ready condition to allow
|
|
// azure to be happy with the node and let it on it's lawn.
|
|
func linuxAgent() (err error) {
|
|
var gs *GoalState
|
|
|
|
gs, err = goalState()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return reportHealth(gs.Incarnation, gs.Container.ContainerID, gs.Container.RoleInstanceList.RoleInstance.InstanceID)
|
|
}
|
|
|
|
func goalState() (gs *GoalState, err error) {
|
|
u, err := url.Parse(AzureInternalEndpoint + "/machine/?comp=goalstate")
|
|
if err != nil {
|
|
return gs, nil
|
|
}
|
|
|
|
req, err := http.NewRequest("GET", u.String(), nil)
|
|
if err != nil {
|
|
return gs, err
|
|
}
|
|
|
|
addHeaders(req)
|
|
|
|
client := &http.Client{}
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return gs, err
|
|
}
|
|
|
|
// nolint: errcheck
|
|
defer resp.Body.Close()
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return gs, err
|
|
}
|
|
|
|
gs = &GoalState{}
|
|
err = xml.Unmarshal(body, gs)
|
|
|
|
return gs, err
|
|
}
|
|
|
|
func reportHealth(gsIncarnation, gsContainerID, gsInstanceID string) (err error) {
|
|
// Construct health response
|
|
h := &Health{
|
|
Xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
|
Xsd: "http://www.w3.org/2001/XMLSchema",
|
|
WAAgent: WAAgent{
|
|
GoalStateIncarnation: gsIncarnation,
|
|
Container: &Container{
|
|
ContainerID: gsContainerID,
|
|
RoleInstanceList: &RoleInstanceList{
|
|
Role: &RoleInstance{
|
|
InstanceID: gsInstanceID,
|
|
Health: &HealthStatus{
|
|
State: "Ready",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Encode health response as xml
|
|
b := new(bytes.Buffer)
|
|
b.WriteString(xml.Header)
|
|
|
|
err = xml.NewEncoder(b).Encode(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var u *url.URL
|
|
|
|
u, err = url.Parse(AzureInternalEndpoint + "/machine/?comp=health")
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
var (
|
|
req *http.Request
|
|
resp *http.Response
|
|
)
|
|
|
|
req, err = http.NewRequest("POST", u.String(), b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
addHeaders(req)
|
|
|
|
client := &http.Client{}
|
|
|
|
resp, err = client.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// TODO probably should do some better check here ( verify status code )
|
|
// nolint: errcheck
|
|
defer resp.Body.Close()
|
|
|
|
_, err = ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func addHeaders(req *http.Request) {
|
|
req.Header.Add("x-ms-agent-name", "WALinuxAgent")
|
|
req.Header.Add("x-ms-version", "2015-04-05")
|
|
req.Header.Add("Content-Type", "text/xml;charset=utf-8")
|
|
}
|
|
|
|
// GoalState is the response from the Azure platform when a machine
|
|
// starts up. Ref:
|
|
// https://github.com/Azure/WALinuxAgent/blob/b26feb7822f7d4a19507b6762fe1bd280c2ba2de/bin/waagent2.0#L4331
|
|
// https://github.com/Azure/WALinuxAgent/blob/3be3e1fbf2330303f76961b87d891672e847ce4e/azurelinuxagent/common/protocol/wire.py#L216
|
|
type GoalState struct {
|
|
XMLName xml.Name `xml:"GoalState"`
|
|
Xsi string `xml:"xsi,attr"`
|
|
Xsd string `xml:"xsd,attr"`
|
|
WAAgent
|
|
}
|
|
|
|
// Health is the response from the local machine to Azure to denote current
|
|
// machine state.
|
|
type Health struct {
|
|
XMLName xml.Name `xml:"Health"`
|
|
Xsi string `xml:"xmlns:xsi,attr"`
|
|
Xsd string `xml:"xmlns:xsd,attr"`
|
|
WAAgent
|
|
}
|
|
|
|
// WAAgent contains the meat of the data format that is passed between the
|
|
// Azure platform and the machine.
|
|
// Mostly, we just care about the Incarnation and Container fields here.
|
|
type WAAgent struct {
|
|
Text string `xml:",chardata"`
|
|
Version string `xml:"Version,omitempty"`
|
|
Incarnation string `xml:"Incarnation,omitempty"`
|
|
GoalStateIncarnation string `xml:"GoalStateIncarnation,omitempty"`
|
|
Machine *Machine `xml:"Machine,omitempty"`
|
|
Container *Container `xml:"Container,omitempty"`
|
|
}
|
|
|
|
// Container holds the interesting details about a provisioned machine.
|
|
type Container struct {
|
|
Text string `xml:",chardata"`
|
|
ContainerID string `xml:"ContainerId"`
|
|
RoleInstanceList *RoleInstanceList `xml:"RoleInstanceList"`
|
|
}
|
|
|
|
// RoleInstanceList is a list but only has a single item which is cool I guess.
|
|
type RoleInstanceList struct {
|
|
Text string `xml:",chardata"`
|
|
RoleInstance *RoleInstance `xml:"RoleInstance,omitempty"`
|
|
Role *RoleInstance `xml:"Role,omitempty"`
|
|
}
|
|
|
|
// RoleInstance contains the specifics for the provisioned VM.
|
|
type RoleInstance struct {
|
|
Text string `xml:",chardata"`
|
|
InstanceID string `xml:"InstanceId"`
|
|
State string `xml:"State,omitempty"`
|
|
Configuration *Configuration `xml:"Configuration,omitempty"`
|
|
Health *HealthStatus `xml:"Health,omitempty"`
|
|
}
|
|
|
|
// Configuration seems important but isnt really used right now. We could
|
|
// very well not include it because we have no use for it right now, but
|
|
// since we want completeness, we're going to include it.
|
|
type Configuration struct {
|
|
Text string `xml:",chardata"`
|
|
HostingEnvironmentConfig string `xml:"HostingEnvironmentConfig"`
|
|
SharedConfig string `xml:"SharedConfig"`
|
|
ExtensionsConfig string `xml:"ExtensionsConfig"`
|
|
FullConfig string `xml:"FullConfig"`
|
|
Certificates string `xml:"Certificates"`
|
|
ConfigName string `xml:"ConfigName"`
|
|
}
|
|
|
|
// Machine holds no useful information for us.
|
|
type Machine struct {
|
|
Text string `xml:",chardata"`
|
|
ExpectedState string `xml:"ExpectedState"`
|
|
StopRolesDeadlineHint string `xml:"StopRolesDeadlineHint"`
|
|
LBProbePorts *struct {
|
|
Text string `xml:",chardata"`
|
|
Port string `xml:"Port"`
|
|
} `xml:"LBProbePorts,omitempty"`
|
|
ExpectHealthReport string `xml:"ExpectHealthReport"`
|
|
}
|
|
|
|
// HealthStatus provides mechanism to trigger Azure to understand that our
|
|
// machine has transitioned to a 'Ready' state and is good to go.
|
|
// We can fill out details if we want to be more verbose...
|
|
type HealthStatus struct {
|
|
Text string `xml:",chardata"`
|
|
State string `xml:"State"`
|
|
Details *struct {
|
|
Text string `xml:",chardata"`
|
|
SubStatus string `xml:"SubStatus"`
|
|
Description string `xml:"Description"`
|
|
} `xml:"Details,omitempty"`
|
|
}
|