mirror of
https://github.com/rancher/os.git
synced 2025-09-08 02:01:27 +00:00
183 lines
4.9 KiB
Go
183 lines
4.9 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"path"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
|
|
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
|
values "github.com/rancher/wrangler/pkg/data"
|
|
"github.com/sirupsen/logrus"
|
|
"gopkg.in/yaml.v3"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
)
|
|
|
|
const defaultName = "m-${System Information/Manufacturer}-${System Information/Product Name}-${System Information/Serial Number}-"
|
|
|
|
var (
|
|
sanitize = regexp.MustCompile("[^0-9a-zA-Z]")
|
|
doubleDash = regexp.MustCompile("--+")
|
|
start = regexp.MustCompile("^[a-zA-Z]")
|
|
)
|
|
|
|
func (i *InventoryServer) register(resp http.ResponseWriter, req *http.Request) {
|
|
machineInventory, machineRegister, data, err := i.buildResponse(req)
|
|
if err != nil {
|
|
http.Error(resp, err.Error(), http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
machine, writer, err := i.authMachine(resp, req, machineInventory.Namespace)
|
|
if err != nil {
|
|
http.Error(resp, err.Error(), http.StatusUnauthorized)
|
|
return
|
|
}
|
|
if writer == nil {
|
|
if err := i.sampleConfig(machineRegister, resp); err != nil {
|
|
http.Error(resp, "authorization required", http.StatusUnauthorized)
|
|
}
|
|
return
|
|
}
|
|
defer writer.Close()
|
|
|
|
if err := i.saveMachine(machine.Spec.TPMHash, machineInventory); err != nil {
|
|
http.Error(resp, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
_, _ = writer.Write(data)
|
|
}
|
|
|
|
func (i *InventoryServer) sampleConfig(machineRegistration *v1.MachineRegistration, writer io.Writer) error {
|
|
_, err := writer.Write([]byte("#cloud-config\n"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return yaml.NewEncoder(writer).Encode(map[string]interface{}{
|
|
"rancheros": map[string]interface{}{
|
|
"install": map[string]interface{}{
|
|
"registrationURL": machineRegistration.Status.RegistrationURL,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func (i *InventoryServer) saveMachine(tpmHash string, machineInventory *v1.MachineInventory) error {
|
|
machines, err := i.machineCache.GetByIndex(tpmHashIndex, tpmHash)
|
|
if err != nil || len(machines) > 0 {
|
|
return err
|
|
}
|
|
|
|
machineInventory.Spec.TPMHash = tpmHash
|
|
_, err = i.machineClient.Create(machineInventory)
|
|
return err
|
|
}
|
|
|
|
func buildName(data map[string]interface{}, name string) string {
|
|
str := name
|
|
result := &strings.Builder{}
|
|
for {
|
|
i := strings.Index(str, "${")
|
|
if i == -1 {
|
|
result.WriteString(str)
|
|
break
|
|
}
|
|
j := strings.Index(str[i:], "}")
|
|
if j == -1 {
|
|
result.WriteString(str)
|
|
break
|
|
}
|
|
|
|
result.WriteString(str[:i])
|
|
obj := values.GetValueN(data, strings.Split(str[i+2:j+i], "/")...)
|
|
if str, ok := obj.(string); ok {
|
|
result.WriteString(str)
|
|
}
|
|
str = str[j+i+1:]
|
|
}
|
|
|
|
resultStr := sanitize.ReplaceAllString(result.String(), "-")
|
|
resultStr = doubleDash.ReplaceAllString(resultStr, "-")
|
|
if !start.MatchString(resultStr) {
|
|
resultStr = "m" + resultStr
|
|
}
|
|
if len(resultStr) > 58 {
|
|
resultStr = resultStr[:58]
|
|
}
|
|
return strings.ToLower(resultStr)
|
|
}
|
|
|
|
func (i *InventoryServer) buildResponse(req *http.Request) (*v1.MachineInventory, *v1.MachineRegistration, []byte, error) {
|
|
token := path.Base(req.URL.Path)
|
|
|
|
smbios, err := getSMBios(req)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
machineRegisters, err := i.machineRegistrationCache.GetByIndex(registrationTokenIndex, token)
|
|
if apierrors.IsNotFound(err) || len(machineRegisters) != 1 {
|
|
if len(machineRegisters) > 1 {
|
|
logrus.Errorf("Multiple MachineRegistrations have the same token %s: %v", token, machineRegisters)
|
|
}
|
|
return nil, nil, nil, err
|
|
}
|
|
machineRegister := machineRegisters[0]
|
|
|
|
name := machineRegister.Spec.MachineName
|
|
if name == "" {
|
|
name = defaultName
|
|
}
|
|
|
|
installConfig := map[string]interface{}{}
|
|
if machineRegister.Spec.CloudConfig != nil && len(machineRegister.Spec.CloudConfig.Data) > 0 {
|
|
installConfig = values.MergeMapsConcatSlice(installConfig, machineRegister.Spec.CloudConfig.Data)
|
|
}
|
|
|
|
serverURL, err := i.serverURL()
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
values.PutValue(installConfig, serverURL, "rancherd", "server")
|
|
values.PutValue(installConfig, "tpm://", "rancherd", "token")
|
|
values.PutValue(installConfig, true, "rancheros", "install", "automatic")
|
|
|
|
data, err := json.Marshal(installConfig)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
return &v1.MachineInventory{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
GenerateName: buildName(smbios, name),
|
|
Namespace: machineRegister.Namespace,
|
|
Labels: machineRegisters[0].Spec.MachineInventoryLabels,
|
|
Annotations: machineRegisters[0].Spec.MachineInventoryAnnotations,
|
|
},
|
|
Spec: v1.MachineInventorySpec{
|
|
SMBIOS: &v1alpha1.GenericMap{
|
|
Data: smbios,
|
|
},
|
|
},
|
|
}, machineRegister, data, nil
|
|
}
|
|
|
|
func getSMBios(req *http.Request) (map[string]interface{}, error) {
|
|
smbios := req.Header.Get("X-Cattle-Smbios")
|
|
if smbios == "" {
|
|
return nil, nil
|
|
}
|
|
smbiosData, err := base64.StdEncoding.DecodeString(smbios)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data := map[string]interface{}{}
|
|
return data, json.Unmarshal(smbiosData, &data)
|
|
}
|