1
0
mirror of https://github.com/rancher/os.git synced 2025-09-08 02:01:27 +00:00
Files
os/pkg/server/register.go
2021-10-29 23:08:26 -07:00

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)
}