mirror of
https://github.com/rancher/os.git
synced 2025-09-06 01:01:43 +00:00
Add TPM and MachineRegister support
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
FROM opensuse/leap:15.3 AS build
|
||||
RUN zypper ref
|
||||
RUN zypper in -y squashfs xorriso go1.16 upx busybox-static curl tar git gzip
|
||||
RUN curl -Lo /usr/bin/luet https://github.com/mudler/luet/releases/download/0.18.1/luet-0.18.1-linux-$(go env GOARCH) && \
|
||||
RUN curl -Lo /usr/bin/luet https://github.com/mudler/luet/releases/download/0.20.5/luet-0.20.5-linux-$(go env GOARCH) && \
|
||||
chmod +x /usr/bin/luet
|
||||
RUN curl -Lo /usr/bin/rancherd https://github.com/rancher/rancherd/releases/download/v0.0.1-alpha10/rancherd-$(go env GOARCH) && \
|
||||
RUN curl -Lo /usr/bin/rancherd https://github.com/rancher/rancherd/releases/download/v0.0.1-alpha11/rancherd-$(go env GOARCH) && \
|
||||
chmod +x /usr/bin/rancherd
|
||||
RUN curl -L https://get.helm.sh/helm-v3.7.1-linux-$(go env GOARCH).tar.gz | tar xzf - -C /usr/bin --strip-components=1
|
||||
COPY go.mod go.sum /usr/src/
|
||||
@@ -90,6 +90,7 @@ RUN zypper in -y \
|
||||
kernel-firmware-qlogic \
|
||||
kernel-firmware-realtek \
|
||||
kernel-firmware-usb-network \
|
||||
libtspi1 \
|
||||
less \
|
||||
lshw \
|
||||
lsof \
|
||||
|
@@ -4,7 +4,7 @@ RUN zypper ref
|
||||
ARG DAPPER_HOST_ARCH
|
||||
ENV ARCH $DAPPER_HOST_ARCH
|
||||
|
||||
RUN zypper in -y bash git gcc docker vim less file curl wget ca-certificates make mkisofs go1.16 qemu-tools
|
||||
RUN zypper in -y bash git gcc docker vim less file curl wget ca-certificates make mkisofs go1.16 qemu-tools trousers-devel
|
||||
RUN go get golang.org/x/tools/cmd/goimports
|
||||
RUN if [ "${ARCH}" == "amd64" ]; then \
|
||||
curl -sL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.40.1; \
|
||||
|
30
Dockerfile.kvm
Normal file
30
Dockerfile.kvm
Normal file
@@ -0,0 +1,30 @@
|
||||
FROM opensuse/leap:15.3
|
||||
RUN zypper ref
|
||||
RUN zypper install -y socat net-tools-deprecated libtasn1-devel gnutls-devel libseccomp-devel json-glib-devel system-user-tss git
|
||||
RUN zypper install -y autoconf
|
||||
RUN zypper install -y automake
|
||||
RUN git clone https://github.com/stefanberger/swtpm.git /usr/src/swtpm
|
||||
RUN zypper install -y libtool
|
||||
RUN zypper install -y gcc
|
||||
RUN zypper install -y libopenssl-devel
|
||||
RUN git clone https://github.com/stefanberger/libtpms.git /usr/src/libtpms
|
||||
RUN zypper install -y gcc-c++
|
||||
RUN zypper install -y make
|
||||
RUN zypper install -y expect
|
||||
RUN zypper install -y sudo
|
||||
RUN cd /usr/src/libtpms && \
|
||||
./autogen.sh --with-openssl --with-tpm2 && \
|
||||
make -j4 && \
|
||||
make install
|
||||
RUN cd /usr/src/swtpm && \
|
||||
./autogen.sh --prefix=/usr --libdir=/usr/lib64 --with-openssl --with-tss-user=root --with-tss-group=tss && \
|
||||
make -j4 && \
|
||||
sudo make -j4 && \
|
||||
sudo make install
|
||||
RUN zypper install -y qemu-x86 qemu-arm qemu-tools
|
||||
|
||||
COPY scripts/qemu-in-container /usr/bin/
|
||||
ENTRYPOINT ["/usr/bin/qemu-in-container"]
|
||||
|
||||
RUN chmod +s /usr/lib/qemu-bridge-helper
|
||||
RUN echo 'allow all' > /etc/qemu/bridge.conf
|
@@ -34,9 +34,14 @@ rules:
|
||||
- apiGroups:
|
||||
- management.cattle.io
|
||||
resources:
|
||||
- 'settings'
|
||||
- 'clusterregistrationtokens'
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- management.cattle.io
|
||||
resources:
|
||||
- 'settings'
|
||||
verbs:
|
||||
- 'get'
|
||||
- 'watch'
|
||||
- 'list'
|
||||
@@ -44,8 +49,6 @@ rules:
|
||||
- apiextensions.k8s.io
|
||||
resources:
|
||||
- customresourcedefinitions
|
||||
#resourceNames:
|
||||
#- managedosimages.rancheros.cattle.io
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
|
@@ -14,6 +14,8 @@ var (
|
||||
automatic = flag.Bool("automatic", false, "Check for and run automatic installation")
|
||||
printConfig = flag.Bool("print-config", false, "Print effective configuration and exit")
|
||||
configFile = flag.String("config-file", "", "Config file to use, local file or http/tftp URL")
|
||||
powerOff = flag.Bool("power-off", false, "Power off after installation")
|
||||
yes = flag.Bool("y", false, "Do not prompt for questions")
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -31,7 +33,7 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := install.Run(*automatic, *configFile); err != nil {
|
||||
if err := install.Run(*automatic, *configFile, *powerOff, *yes); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,3 @@
|
||||
[Unit]
|
||||
ConditionPathExists=!/run/cos/live_mode
|
||||
ConditionPathExists=!/run/cos/rescue_mode
|
||||
ConditionPathExists=!/run/cos/recovery_mode
|
||||
|
@@ -18,5 +18,6 @@ LimitNOFILE=1048576
|
||||
LimitNPROC=infinity
|
||||
LimitCORE=infinity
|
||||
TasksMax=infinity
|
||||
StandardOutput=journal+console
|
||||
TimeoutStartSec=0
|
||||
ExecStart=/usr/bin/rancherd bootstrap
|
||||
|
@@ -15,6 +15,14 @@ spec:
|
||||
EOF
|
||||
}
|
||||
|
||||
if [ -e /etc/rancher/rke2/rke2.yaml ]; then
|
||||
export KUBECONFIG=/etc/rancher/rke2/rke2.yaml
|
||||
elif [ -e /etc/rancher/k3s/k3s.yaml ]; then
|
||||
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
|
||||
PULL_POLICY=IfNotPresent
|
||||
if [ "$IMAGE_TAG" = dev ]; then
|
||||
PULL_POLICY=Always
|
||||
@@ -29,6 +37,11 @@ helm upgrade \
|
||||
--set image.imagePullPolicy=${PULL_POLICY} \
|
||||
rancheros-operator /usr/share/rancher/os2/rancheros-operator-chart.tgz
|
||||
|
||||
while ! kubectl get crd managedosimages.rancheros.cattle.io; do
|
||||
echo Waiting for RancherOS Operator to be running
|
||||
sleep 15
|
||||
done
|
||||
|
||||
while ! manifest | kubectl apply -f -; do
|
||||
sleep 15
|
||||
done
|
||||
|
39
go.mod
39
go.mod
@@ -2,25 +2,58 @@ module github.com/rancher/os2
|
||||
|
||||
go 1.16
|
||||
|
||||
replace (
|
||||
k8s.io/api => k8s.io/api v0.22.2
|
||||
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.22.2
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.22.2
|
||||
k8s.io/apiserver => k8s.io/apiserver v0.22.2
|
||||
k8s.io/cli-runtime => k8s.io/cli-runtime v0.22.2
|
||||
k8s.io/client-go => k8s.io/client-go v0.22.2
|
||||
k8s.io/cloud-provider => k8s.io/cloud-provider v0.22.2
|
||||
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.22.2
|
||||
k8s.io/code-generator => k8s.io/code-generator v0.22.2
|
||||
k8s.io/component-base => k8s.io/component-base v0.22.2
|
||||
k8s.io/component-helpers => k8s.io/component-helpers v0.22.2
|
||||
k8s.io/controller-manager => k8s.io/controller-manager v0.22.2
|
||||
k8s.io/cri-api => k8s.io/cri-api v0.22.2
|
||||
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.22.2
|
||||
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.22.2
|
||||
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.22.2
|
||||
k8s.io/kube-proxy => k8s.io/kube-proxy v0.22.2
|
||||
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.22.2
|
||||
k8s.io/kubectl => k8s.io/kubectl v0.22.2
|
||||
k8s.io/kubelet => k8s.io/kubelet v0.22.2
|
||||
k8s.io/kubernetes => k8s.io/kubernetes v1.22.2
|
||||
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.22.2
|
||||
k8s.io/metrics => k8s.io/metrics v0.22.2
|
||||
k8s.io/mount-utils => k8s.io/mount-utils v0.22.2
|
||||
k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.22.2
|
||||
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.22.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/certificate-transparency-go v1.1.2
|
||||
github.com/google/go-attestation v0.3.2
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/mattn/go-isatty v0.0.12
|
||||
github.com/pin/tftp v2.1.0+incompatible // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rancher/fleet/pkg/apis v0.0.0-20210927195558-4aaa778d23dd
|
||||
github.com/rancher/lasso v0.0.0-20210709145333-6c6cd7fd6607
|
||||
github.com/rancher/rancher/pkg/apis v0.0.0-20211013185633-a636bda2a00e
|
||||
github.com/rancher/rancherd v0.0.1-alpha9.0.20211028172625-bdf5642d62d5
|
||||
github.com/rancher/steve v0.0.0-20210922195510-7224dc21013d
|
||||
github.com/rancher/system-upgrade-controller/pkg/apis v0.0.0-20210929162341-5e6e996d9486
|
||||
github.com/rancher/wrangler v0.8.7
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/tredoe/osutil v1.0.5
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
||||
gopkg.in/pin/tftp.v2 v2.1.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
gotest.tools v2.2.0+incompatible
|
||||
k8s.io/api v0.22.2
|
||||
k8s.io/apimachinery v0.22.2
|
||||
k8s.io/client-go v12.0.0+incompatible
|
||||
sigs.k8s.io/controller-runtime v0.9.0-beta.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
replace k8s.io/client-go => k8s.io/client-go v0.22.2
|
||||
|
@@ -48,6 +48,7 @@ nav:
|
||||
- installation.md
|
||||
- upgrade.md
|
||||
- configuration.md
|
||||
- customizing.md
|
||||
- dashboard.md
|
||||
- operator.md
|
||||
- versions.md
|
||||
|
@@ -1,12 +1,7 @@
|
||||
if [ -z "$KUBECONFIG" ]; then
|
||||
if [ -e /etc/rancher/rke2 ]; then
|
||||
export KUBECONFIG=/etc/rancher/rke2/rke2.yaml
|
||||
fi
|
||||
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
|
||||
fi
|
||||
if [ -d /var/lib/rancher/rke2/bin ]; then
|
||||
export PATH="${PATH}:/var/lib/rancher/rke2/bin"
|
||||
export KUBECONFIG="/etc/rancher/k3s/k3s.yaml:/etc/rancher/rke2/rke2.yaml"
|
||||
fi
|
||||
export PATH="${PATH}:/var/lib/rancher/rke2/bin"
|
||||
if [ -z "$CONTAINER_RUNTIME_ENDPOINT" ]; then
|
||||
export CONTAINER_RUNTIME_ENDPOINT=unix:///var/run/k3s/containerd/containerd.sock
|
||||
fi
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
|
||||
"github.com/rancher/wrangler/pkg/genericcondition"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
@@ -8,6 +10,30 @@ import (
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
type MachineRegistration struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec MachineRegistrationSpec `json:"spec"`
|
||||
Status MachineRegistrationStatus `json:"status"`
|
||||
}
|
||||
|
||||
type MachineRegistrationSpec struct {
|
||||
MachineName string `json:"machineName,omitempty"`
|
||||
MachineInventoryLabels map[string]string `json:"machineInventoryLabels,omitempty"`
|
||||
MachineInventoryAnnotations map[string]string `json:"machineInventoryAnnotations,omitempty"`
|
||||
CloudConfig *v1alpha1.GenericMap `json:"cloudConfig,omitempty"`
|
||||
}
|
||||
|
||||
type MachineRegistrationStatus struct {
|
||||
Conditions []genericcondition.GenericCondition `json:"conditions,omitempty"`
|
||||
RegistrationURL string `json:"registrationURL,omitempty"`
|
||||
RegistrationToken string `json:"registrationToken,omitempty"`
|
||||
}
|
||||
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
type MachineInventory struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
@@ -17,19 +43,20 @@ type MachineInventory struct {
|
||||
}
|
||||
|
||||
type MachineInventorySpec struct {
|
||||
ClusterName string `json:"clusterName,omitempty"`
|
||||
TPMHash string `json:"tpmHash,omitempty"`
|
||||
SMBIOS *v1alpha1.GenericMap `json:"smbios,omitempty"`
|
||||
ClusterName string `json:"clusterName"`
|
||||
MachineTokenSecretName string `json:"machineTokenSecretName,omitempty"`
|
||||
Config MachineRuntimeConfig `json:"config,omitempty"`
|
||||
}
|
||||
|
||||
type MachineRuntimeConfig struct {
|
||||
Role string `json:"role,omitempty"`
|
||||
Role string `json:"role"`
|
||||
NodeName string `json:"nodeName,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
InternalAddress string `json:"internalAddress,omitempty"`
|
||||
Taints []corev1.Taint `json:"taints,omitempty"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
ConfigValues map[string]string `json:"extraConfig,omitempty"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
}
|
||||
|
||||
type MachineInventoryStatus struct {
|
||||
|
@@ -23,6 +23,7 @@ package v1
|
||||
import (
|
||||
v1alpha1 "github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
|
||||
upgradecattleiov1 "github.com/rancher/system-upgrade-controller/pkg/apis/upgrade.cattle.io/v1"
|
||||
genericcondition "github.com/rancher/wrangler/pkg/genericcondition"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -92,6 +93,10 @@ func (in *MachineInventoryList) DeepCopyObject() runtime.Object {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MachineInventorySpec) DeepCopyInto(out *MachineInventorySpec) {
|
||||
*out = *in
|
||||
if in.SMBIOS != nil {
|
||||
in, out := &in.SMBIOS, &out.SMBIOS
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
in.Config.DeepCopyInto(&out.Config)
|
||||
return
|
||||
}
|
||||
@@ -122,6 +127,122 @@ func (in *MachineInventoryStatus) DeepCopy() *MachineInventoryStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MachineRegistration) DeepCopyInto(out *MachineRegistration) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineRegistration.
|
||||
func (in *MachineRegistration) DeepCopy() *MachineRegistration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MachineRegistration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *MachineRegistration) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MachineRegistrationList) DeepCopyInto(out *MachineRegistrationList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]MachineRegistration, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineRegistrationList.
|
||||
func (in *MachineRegistrationList) DeepCopy() *MachineRegistrationList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MachineRegistrationList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *MachineRegistrationList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MachineRegistrationSpec) DeepCopyInto(out *MachineRegistrationSpec) {
|
||||
*out = *in
|
||||
if in.MachineInventoryLabels != nil {
|
||||
in, out := &in.MachineInventoryLabels, &out.MachineInventoryLabels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.MachineInventoryAnnotations != nil {
|
||||
in, out := &in.MachineInventoryAnnotations, &out.MachineInventoryAnnotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.CloudConfig != nil {
|
||||
in, out := &in.CloudConfig, &out.CloudConfig
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineRegistrationSpec.
|
||||
func (in *MachineRegistrationSpec) DeepCopy() *MachineRegistrationSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MachineRegistrationSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MachineRegistrationStatus) DeepCopyInto(out *MachineRegistrationStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]genericcondition.GenericCondition, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineRegistrationStatus.
|
||||
func (in *MachineRegistrationStatus) DeepCopy() *MachineRegistrationStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MachineRegistrationStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MachineRuntimeConfig) DeepCopyInto(out *MachineRuntimeConfig) {
|
||||
*out = *in
|
||||
@@ -139,13 +260,6 @@ func (in *MachineRuntimeConfig) DeepCopyInto(out *MachineRuntimeConfig) {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.ConfigValues != nil {
|
||||
in, out := &in.ConfigValues, &out.ConfigValues
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@@ -43,6 +43,23 @@ func NewMachineInventory(namespace, name string, obj MachineInventory) *MachineI
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// MachineRegistrationList is a list of MachineRegistration resources
|
||||
type MachineRegistrationList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata"`
|
||||
|
||||
Items []MachineRegistration `json:"items"`
|
||||
}
|
||||
|
||||
func NewMachineRegistration(namespace, name string, obj MachineRegistration) *MachineRegistration {
|
||||
obj.APIVersion, obj.Kind = SchemeGroupVersion.WithKind("MachineRegistration").ToAPIVersionAndKind()
|
||||
obj.Name = name
|
||||
obj.Namespace = namespace
|
||||
return &obj
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// ManagedOSImageList is a list of ManagedOSImage resources
|
||||
type ManagedOSImageList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
@@ -29,6 +29,7 @@ import (
|
||||
|
||||
var (
|
||||
MachineInventoryResourceName = "machineinventories"
|
||||
MachineRegistrationResourceName = "machineregistrations"
|
||||
ManagedOSImageResourceName = "managedosimages"
|
||||
)
|
||||
|
||||
@@ -55,6 +56,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&MachineInventory{},
|
||||
&MachineInventoryList{},
|
||||
&MachineRegistration{},
|
||||
&MachineRegistrationList{},
|
||||
&ManagedOSImage{},
|
||||
&ManagedOSImageList{},
|
||||
)
|
||||
|
@@ -18,11 +18,14 @@ type Install struct {
|
||||
Token string `json:"-"`
|
||||
Role string `json:"-"`
|
||||
Password string `json:"password,omitempty"`
|
||||
RegistrationURL string `json:"registrationUrl,omitempty"`
|
||||
RegistrationCACert string `json:"registrationCaCert,omitempty"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty"`
|
||||
RancherOS RancherOS `json:"rancheros,omitempty"`
|
||||
Data map[string]interface{} `json:"-"`
|
||||
}
|
||||
|
||||
type YipConfig struct {
|
||||
|
@@ -1,16 +1,22 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rancher/os2/pkg/dmidecode"
|
||||
"github.com/rancher/rancherd/pkg/tpm"
|
||||
values "github.com/rancher/wrangler/pkg/data"
|
||||
"github.com/rancher/wrangler/pkg/data/convert"
|
||||
schemas2 "github.com/rancher/wrangler/pkg/schemas"
|
||||
"github.com/sirupsen/logrus"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
@@ -60,30 +66,33 @@ func readFileFunc(path string) func() (map[string]interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func readNested(data map[string]interface{}) (map[string]interface{}, error) {
|
||||
func readNested(data map[string]interface{}, overlay bool) (map[string]interface{}, error) {
|
||||
var (
|
||||
nestedConfigFiles = convert.ToStringSlice(values.GetValueN(data, "rancheros", "install", "configUrl"))
|
||||
funcs []reader
|
||||
)
|
||||
|
||||
if overlay {
|
||||
funcs = append(funcs, func() (map[string]interface{}, error) {
|
||||
return data, nil
|
||||
})
|
||||
}
|
||||
|
||||
for _, nestedConfigFile := range nestedConfigFiles {
|
||||
funcs = append(funcs, readFileFunc(nestedConfigFile))
|
||||
}
|
||||
|
||||
if !overlay {
|
||||
funcs = append(funcs, func() (map[string]interface{}, error) {
|
||||
return data, nil
|
||||
})
|
||||
}
|
||||
|
||||
return merge(funcs...)
|
||||
}
|
||||
|
||||
func readFile(path string) (result map[string]interface{}, _ error) {
|
||||
result = map[string]interface{}{}
|
||||
defer func() {
|
||||
if v, ok := result["install"]; ok {
|
||||
values.PutValue(result, v, "rancheros", "install")
|
||||
}
|
||||
}()
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(path, "http://"):
|
||||
@@ -117,7 +126,7 @@ func readFile(path string) (result map[string]interface{}, _ error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return readNested(data)
|
||||
return readNested(data, false)
|
||||
}
|
||||
|
||||
type reader func() (map[string]interface{}, error)
|
||||
@@ -145,6 +154,20 @@ func readConfigMap(cfg string) (map[string]interface{}, error) {
|
||||
if cfg != "" {
|
||||
values.PutValue(data, cfg, "rancheros", "install", "configUrl")
|
||||
}
|
||||
|
||||
registrationURL := convert.ToString(values.GetValueN(data, "rancheros", "install", "registrationUrl"))
|
||||
registrationCA := convert.ToString(values.GetValueN(data, "rancheros", "install", "registrationCaCert"))
|
||||
if registrationURL != "" {
|
||||
for {
|
||||
newData, err := returnRegistrationData(registrationURL, registrationCA)
|
||||
if err == nil {
|
||||
return newData, nil
|
||||
}
|
||||
logrus.Errorf("failed to read registration URL %s, retrying: %v", registrationURL, err)
|
||||
time.Sleep(15 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@@ -157,12 +180,7 @@ func ToFile(cfg Config, output string) error {
|
||||
}
|
||||
|
||||
func ToBytes(cfg Config) ([]byte, error) {
|
||||
data, err := merge(readFileFunc(cfg.RancherOS.Install.ConfigURL), func() (map[string]interface{}, error) {
|
||||
return convert.EncodeToMap(cfg)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := values.MergeMaps(nil, cfg.Data)
|
||||
values.RemoveValue(data, "install")
|
||||
values.RemoveValue(data, "rancheros", "install")
|
||||
bytes, err := yaml.Marshal(data)
|
||||
@@ -179,7 +197,41 @@ func ReadConfig(cfg string) (result Config, err error) {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, convert.ToObj(data, &result)
|
||||
if err := convert.ToObj(data, &result); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.Data = data
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func returnRegistrationData(url, ca string) (map[string]interface{}, error) {
|
||||
smbios, err := getSMBiosHeaders()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := tpm.Get([]byte(ca), url, smbios)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logrus.Infof("Retrieved config from registrationURL: %s", data)
|
||||
result := map[string]interface{}{}
|
||||
return result, json.Unmarshal(data, &result)
|
||||
}
|
||||
|
||||
func getSMBiosHeaders() (http.Header, error) {
|
||||
smbios, err := dmidecode.Decode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
smbiosData, err := json.Marshal(smbios)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
header := http.Header{}
|
||||
header.Set("X-Cattle-Smbios", base64.StdEncoding.EncodeToString(smbiosData))
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func readCmdline() (map[string]interface{}, error) {
|
||||
@@ -221,5 +273,9 @@ func readCmdline() (map[string]interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return readNested(data)
|
||||
if err := schema.Mapper.ToInternal(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return readNested(data, true)
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ type FuzzyNames struct {
|
||||
|
||||
func (f *FuzzyNames) ToInternal(data data.Object) error {
|
||||
for k, v := range data {
|
||||
if newK, ok := f.names[k]; ok && newK != k {
|
||||
if newK, ok := f.names[strings.ToLower(k)]; ok && newK != k {
|
||||
data[newK] = v
|
||||
}
|
||||
}
|
||||
|
@@ -43,7 +43,7 @@ func (h *handler) OnMachineInventoryRemove(key string, machine *v1.MachineInvent
|
||||
}
|
||||
|
||||
func (h *handler) OnMachineInventory(machine *v1.MachineInventory, status v1.MachineInventoryStatus) (v1.MachineInventoryStatus, error) {
|
||||
if machine == nil {
|
||||
if machine == nil || machine.Spec.ClusterName == "" {
|
||||
return status, nil
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func (h *handler) OnMachineInventory(machine *v1.MachineInventory, status v1.Mac
|
||||
return status, fmt.Errorf("waiting for mgmt cluster to be created for prov cluster %s/%s", machine.Namespace, machine.Spec.ClusterName)
|
||||
}
|
||||
|
||||
crtName := name.SafeConcatName(cluster.Status.ClusterName, machine.Name, "-token")
|
||||
crtName := name.SafeConcatName(cluster.Status.ClusterName, machine.Name, "token")
|
||||
_, err = h.clusterRegistrationTokenCache.Get(cluster.Status.ClusterName, crtName)
|
||||
if apierrors.IsNotFound(err) {
|
||||
_, err = h.clusterRegistrationTokenClient.Create(&v3.ClusterRegistrationToken{
|
||||
|
52
pkg/controllers/registration/registration.go
Normal file
52
pkg/controllers/registration/registration.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package registration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
||||
"github.com/rancher/os2/pkg/clients"
|
||||
ranchercontrollers "github.com/rancher/os2/pkg/generated/controllers/management.cattle.io/v3"
|
||||
roscontrollers "github.com/rancher/os2/pkg/generated/controllers/rancheros.cattle.io/v1"
|
||||
"github.com/rancher/wrangler/pkg/randomtoken"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
settingsCache ranchercontrollers.SettingCache
|
||||
}
|
||||
|
||||
func Register(ctx context.Context, clients *clients.Clients) {
|
||||
h := handler{
|
||||
settingsCache: clients.Rancher.Setting().Cache(),
|
||||
}
|
||||
roscontrollers.RegisterMachineRegistrationStatusHandler(ctx, clients.OS.MachineRegistration(), "Ready", "machine-registration",
|
||||
h.OnChange)
|
||||
}
|
||||
|
||||
func (h *handler) OnChange(obj *v1.MachineRegistration, status v1.MachineRegistrationStatus) (v1.MachineRegistrationStatus, error) {
|
||||
serverURL, err := h.serverURL()
|
||||
if err != nil {
|
||||
return status, err
|
||||
}
|
||||
|
||||
if status.RegistrationToken == "" {
|
||||
status.RegistrationToken, err = randomtoken.Generate()
|
||||
if err != nil {
|
||||
return status, err
|
||||
}
|
||||
}
|
||||
|
||||
status.RegistrationURL = serverURL + "/v1-rancheros/registration/" + status.RegistrationToken
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func (h *handler) serverURL() (string, error) {
|
||||
setting, err := h.settingsCache.Get("server-url")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if setting.Value == "" {
|
||||
return "", fmt.Errorf("server-url is not set")
|
||||
}
|
||||
return setting.Value, nil
|
||||
}
|
78
pkg/dmidecode/decode.go
Normal file
78
pkg/dmidecode/decode.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package dmidecode
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
values "github.com/rancher/wrangler/pkg/data"
|
||||
"github.com/rancher/wrangler/pkg/kv"
|
||||
)
|
||||
|
||||
func Decode() (map[string]interface{}, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
cmd := exec.Command("dmidecode")
|
||||
cmd.Stdout = buf
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, fmt.Errorf("looking up SMBIOS tables (using dmidecode): %w", err)
|
||||
}
|
||||
|
||||
return dmiOutputToMap(buf), nil
|
||||
}
|
||||
|
||||
func dmiOutputToMap(buf io.Reader) map[string]interface{} {
|
||||
var (
|
||||
result = map[string]interface{}{}
|
||||
scanner = bufio.NewScanner(buf)
|
||||
start = false
|
||||
lastKey []string
|
||||
stopLines = map[string]bool{
|
||||
"OEM-specific Type": true,
|
||||
"End Of Table": true,
|
||||
}
|
||||
)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "Handle ") {
|
||||
start = true
|
||||
continue
|
||||
} else if strings.TrimSpace(line) == "" || !start || stopLines[line] {
|
||||
start = false
|
||||
continue
|
||||
}
|
||||
|
||||
var key []string
|
||||
for strings.HasPrefix(line, "\t") {
|
||||
line = strings.TrimPrefix(line, "\t")
|
||||
if len(lastKey) > len(key) {
|
||||
key = append(key, lastKey[len(key)])
|
||||
}
|
||||
}
|
||||
name, value := kv.Split(line, ": ")
|
||||
key = append(key, name)
|
||||
|
||||
if strings.TrimSpace(value) != "" || strings.Contains(line, ":") {
|
||||
values.PutValue(result, value, key...)
|
||||
} else if len(key) > 1 {
|
||||
parentKey := key[:len(key)-1]
|
||||
parentValue := values.GetValueN(result, parentKey...)
|
||||
if parentSlice, ok := parentValue.([]interface{}); ok {
|
||||
parentValue = append(parentSlice, name)
|
||||
} else {
|
||||
parentValue = []interface{}{name}
|
||||
}
|
||||
values.PutValue(result, parentValue, parentKey...)
|
||||
} else {
|
||||
values.PutValue(result, map[string]interface{}{}, key...)
|
||||
}
|
||||
|
||||
lastKey = key
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
945
pkg/dmidecode/decode_test.go
Normal file
945
pkg/dmidecode/decode_test.go
Normal file
@@ -0,0 +1,945 @@
|
||||
package dmidecode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
var testInput = `
|
||||
# dmidecode 3.2
|
||||
Getting SMBIOS data from sysfs.
|
||||
SMBIOS 3.1.1 present.
|
||||
Table at 0xACE76000.
|
||||
|
||||
Handle 0x0000, DMI type 222, 14 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
DE 0E 00 00 01 99 00 03 10 01 20 02 30 03
|
||||
Strings:
|
||||
Memory Init Complete
|
||||
End of DXE Phase
|
||||
BIOS Boot Complete
|
||||
|
||||
Handle 0x0001, DMI type 14, 8 bytes
|
||||
Group Associations
|
||||
Name: Intel(R) Silicon View Technology
|
||||
Items: 1
|
||||
0x0000 (OEM-specific)
|
||||
|
||||
Handle 0x0002, DMI type 134, 13 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
86 0D 02 00 21 02 20 20 00 00 00 00 00
|
||||
|
||||
Handle 0x0003, DMI type 16, 23 bytes
|
||||
Physical Memory Array
|
||||
Location: System Board Or Motherboard
|
||||
Use: System Memory
|
||||
Error Correction Type: None
|
||||
Maximum Capacity: 64 GB
|
||||
Error Information Handle: Not Provided
|
||||
Number Of Devices: 4
|
||||
|
||||
Handle 0x0004, DMI type 17, 40 bytes
|
||||
Memory Device
|
||||
Array Handle: 0x0003
|
||||
Error Information Handle: Not Provided
|
||||
Total Width: 64 bits
|
||||
Data Width: 64 bits
|
||||
Size: 32 GB
|
||||
Form Factor: SODIMM
|
||||
Set: None
|
||||
Locator: ChannelA-DIMM0
|
||||
Bank Locator: BANK 0
|
||||
Type: DDR4
|
||||
Type Detail: Synchronous
|
||||
Speed: 2667 MT/s
|
||||
Manufacturer: Micron
|
||||
Serial Number: 25CD9D0D
|
||||
Asset Tag: None
|
||||
Part Number: 16ATF4G64HZ-2G6B2
|
||||
Rank: 2
|
||||
Configured Memory Speed: 2667 MT/s
|
||||
Minimum Voltage: Unknown
|
||||
Maximum Voltage: Unknown
|
||||
Configured Voltage: 1.2 V
|
||||
|
||||
Handle 0x0005, DMI type 17, 40 bytes
|
||||
Memory Device
|
||||
Array Handle: 0x0003
|
||||
Error Information Handle: Not Provided
|
||||
Total Width: 64 bits
|
||||
Data Width: 64 bits
|
||||
Size: 32 GB
|
||||
Form Factor: SODIMM
|
||||
Set: None
|
||||
Locator: ChannelB-DIMM0
|
||||
Bank Locator: BANK 2
|
||||
Type: DDR4
|
||||
Type Detail: Synchronous
|
||||
Speed: 2667 MT/s
|
||||
Manufacturer: Micron
|
||||
Serial Number: XXXXXXXX
|
||||
Asset Tag: None
|
||||
Part Number: 16ATF4G64HZ-2G6B2
|
||||
Rank: 2
|
||||
Configured Memory Speed: 2667 MT/s
|
||||
Minimum Voltage: Unknown
|
||||
Maximum Voltage: Unknown
|
||||
Configured Voltage: 1.2 V
|
||||
|
||||
Handle 0x0006, DMI type 19, 31 bytes
|
||||
Memory Array Mapped Address
|
||||
Starting Address: 0x00000000000
|
||||
Ending Address: 0x00FFFFFFFFF
|
||||
Range Size: 64 GB
|
||||
Physical Array Handle: 0x0003
|
||||
Partition Width: 2
|
||||
|
||||
Handle 0x0007, DMI type 221, 12 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
DD 0C 07 00 01 01 00 02 00 00 BD 10
|
||||
Strings:
|
||||
BIOS Guard
|
||||
|
||||
Handle 0x0008, DMI type 221, 26 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
DD 1A 08 00 03 01 00 07 00 68 40 00 02 00 00 00
|
||||
00 D6 00 03 00 01 06 00 00 00
|
||||
Strings:
|
||||
Reference Code - CPU
|
||||
uCode Version
|
||||
TXT ACM version
|
||||
|
||||
Handle 0x0009, DMI type 221, 26 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
DD 1A 09 00 03 01 00 07 00 68 40 00 02 00 0C 00
|
||||
00 0A 00 03 04 0C 00 46 74 06
|
||||
Strings:
|
||||
Reference Code - ME
|
||||
MEBx version
|
||||
ME Firmware Version
|
||||
Corporate SKU
|
||||
|
||||
Handle 0x000A, DMI type 221, 82 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
DD 52 0A 00 0B 01 00 07 00 68 40 00 02 03 FF FF
|
||||
FF FF FF 04 00 FF FF FF 10 00 05 00 FF FF FF 10
|
||||
00 06 00 02 0A 00 00 00 07 00 02 00 00 00 00 08
|
||||
00 09 00 00 00 00 09 00 0A 00 00 00 00 0A 00 07
|
||||
00 00 00 00 0B 00 06 00 00 00 00 0C 00 07 00 00
|
||||
00 00
|
||||
Strings:
|
||||
Reference Code - CNL PCH
|
||||
PCH-CRID Status
|
||||
Disabled
|
||||
PCH-CRID Original Value
|
||||
PCH-CRID New Value
|
||||
OPROM - RST - RAID
|
||||
CNL PCH H A0 Hsio Version
|
||||
CNL PCH H Ax Hsio Version
|
||||
CNL PCH H Bx Hsio Version
|
||||
CNL PCH LP B0 Hsio Version
|
||||
CNL PCH LP Bx Hsio Version
|
||||
CNL PCH LP Dx Hsio Version
|
||||
|
||||
Handle 0x000B, DMI type 221, 54 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
DD 36 0B 00 07 01 00 07 00 68 40 00 02 00 00 07
|
||||
01 6E 00 03 00 07 00 68 40 00 04 05 FF FF FF FF
|
||||
FF 06 00 00 00 00 0D 00 07 00 00 00 00 0D 00 08
|
||||
00 FF FF FF FF FF
|
||||
Strings:
|
||||
Reference Code - SA - System Agent
|
||||
Reference Code - MRC
|
||||
SA - PCIe Version
|
||||
SA-CRID Status
|
||||
Enabled
|
||||
SA-CRID Original Value
|
||||
SA-CRID New Value
|
||||
OPROM - VBIOS
|
||||
|
||||
Handle 0x000C, DMI type 221, 12 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
DD 0C 0C 00 01 01 00 04 00 00 00 00
|
||||
Strings:
|
||||
FSP Binary Version
|
||||
|
||||
Handle 0x000D, DMI type 7, 27 bytes
|
||||
Cache Information
|
||||
Socket Designation: L1 Cache
|
||||
Configuration: Enabled, Not Socketed, Level 1
|
||||
Operational Mode: Write Back
|
||||
Location: Internal
|
||||
Installed Size: 512 kB
|
||||
Maximum Size: 512 kB
|
||||
Supported SRAM Types:
|
||||
Synchronous
|
||||
Installed SRAM Type: Synchronous
|
||||
Speed: Unknown
|
||||
Error Correction Type: Parity
|
||||
System Type: Unified
|
||||
Associativity: 8-way Set-associative
|
||||
|
||||
Handle 0x000E, DMI type 7, 27 bytes
|
||||
Cache Information
|
||||
Socket Designation: L2 Cache
|
||||
Configuration: Enabled, Not Socketed, Level 2
|
||||
Operational Mode: Write Back
|
||||
Location: Internal
|
||||
Installed Size: 2048 kB
|
||||
Maximum Size: 2048 kB
|
||||
Supported SRAM Types:
|
||||
Synchronous
|
||||
Installed SRAM Type: Synchronous
|
||||
Speed: Unknown
|
||||
Error Correction Type: Single-bit ECC
|
||||
System Type: Unified
|
||||
Associativity: 4-way Set-associative
|
||||
|
||||
Handle 0x000F, DMI type 7, 27 bytes
|
||||
Cache Information
|
||||
Socket Designation: L3 Cache
|
||||
Configuration: Enabled, Not Socketed, Level 3
|
||||
Operational Mode: Write Back
|
||||
Location: Internal
|
||||
Installed Size: 16384 kB
|
||||
Maximum Size: 16384 kB
|
||||
Supported SRAM Types:
|
||||
Synchronous
|
||||
Installed SRAM Type: Synchronous
|
||||
Speed: Unknown
|
||||
Error Correction Type: Multi-bit ECC
|
||||
System Type: Unified
|
||||
Associativity: 16-way Set-associative
|
||||
|
||||
Handle 0x0010, DMI type 4, 48 bytes
|
||||
Processor Information
|
||||
Socket Designation: U3E1
|
||||
Type: Central Processor
|
||||
Family: Core i9
|
||||
Manufacturer: Intel(R) Corporation
|
||||
ID: ED 06 09 00 FF FB EB BF
|
||||
Signature: Type 0, Family 6, Model 158, Stepping 13
|
||||
Flags:
|
||||
FPU (Floating-point unit on-chip)
|
||||
VME (Virtual mode extension)
|
||||
DE (Debugging extension)
|
||||
PSE (Page size extension)
|
||||
TSC (Time stamp counter)
|
||||
MSR (Model specific registers)
|
||||
PAE (Physical address extension)
|
||||
MCE (Machine check exception)
|
||||
CX8 (CMPXCHG8 instruction supported)
|
||||
APIC (On-chip APIC hardware supported)
|
||||
SEP (Fast system call)
|
||||
MTRR (Memory type range registers)
|
||||
PGE (Page global enable)
|
||||
MCA (Machine check architecture)
|
||||
CMOV (Conditional move instruction supported)
|
||||
PAT (Page attribute table)
|
||||
PSE-36 (36-bit page size extension)
|
||||
CLFSH (CLFLUSH instruction supported)
|
||||
DS (Debug store)
|
||||
ACPI (ACPI supported)
|
||||
MMX (MMX technology supported)
|
||||
FXSR (FXSAVE and FXSTOR instructions supported)
|
||||
SSE (Streaming SIMD extensions)
|
||||
SSE2 (Streaming SIMD extensions 2)
|
||||
SS (Self-snoop)
|
||||
HTT (Multi-threading)
|
||||
TM (Thermal monitor supported)
|
||||
PBE (Pending break enabled)
|
||||
Version: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
|
||||
Voltage: 0.8 V
|
||||
External Clock: 100 MHz
|
||||
Max Speed: 2300 MHz
|
||||
Current Speed: 2300 MHz
|
||||
Status: Populated, Enabled
|
||||
Upgrade: Socket BGA1440
|
||||
L1 Cache Handle: 0x000D
|
||||
L2 Cache Handle: 0x000E
|
||||
L3 Cache Handle: 0x000F
|
||||
Serial Number: None
|
||||
Asset Tag: None
|
||||
Part Number: None
|
||||
Core Count: 8
|
||||
Core Enabled: 8
|
||||
Thread Count: 16
|
||||
Characteristics:
|
||||
64-bit capable
|
||||
Multi-Core
|
||||
Hardware Thread
|
||||
Execute Protection
|
||||
Enhanced Virtualization
|
||||
Power/Performance Control
|
||||
|
||||
Handle 0x0011, DMI type 0, 26 bytes
|
||||
BIOS Information
|
||||
Vendor: LENOVO
|
||||
Version: N2OET47W (1.34 )
|
||||
Release Date: 08/06/2020
|
||||
Address: 0xE0000
|
||||
Runtime Size: 128 kB
|
||||
ROM Size: 32 MB
|
||||
Characteristics:
|
||||
PCI is supported
|
||||
PNP is supported
|
||||
BIOS is upgradeable
|
||||
BIOS shadowing is allowed
|
||||
Boot from CD is supported
|
||||
Selectable boot is supported
|
||||
EDD is supported
|
||||
3.5"/720 kB floppy services are supported (int 13h)
|
||||
Print screen service is supported (int 5h)
|
||||
8042 keyboard services are supported (int 9h)
|
||||
Serial services are supported (int 14h)
|
||||
Printer services are supported (int 17h)
|
||||
CGA/mono video services are supported (int 10h)
|
||||
ACPI is supported
|
||||
USB legacy is supported
|
||||
BIOS boot specification is supported
|
||||
Targeted content distribution is supported
|
||||
UEFI is supported
|
||||
BIOS Revision: 1.34
|
||||
Firmware Revision: 1.23
|
||||
|
||||
Handle 0x0012, DMI type 1, 27 bytes
|
||||
System Information
|
||||
Manufacturer: LENOVO
|
||||
Product Name: 20QTS00Y00
|
||||
Version: ThinkPad P1 Gen 2
|
||||
Serial Number: XXXXXXXX
|
||||
UUID: 069a9e4c-2eec-11b2-a85c-e9c6a8e86998
|
||||
Wake-up Type: Power Switch
|
||||
SKU Number: LENOVO_MT_20QT_BU_Think_FM_ThinkPad P1 Gen 2
|
||||
Family: ThinkPad P1 Gen 2
|
||||
|
||||
Handle 0x0013, DMI type 2, 15 bytes
|
||||
Base Board Information
|
||||
Manufacturer: LENOVO
|
||||
Product Name: 20QTS00Y00
|
||||
Version: SDK0T08861 WIN
|
||||
Serial Number: XXXXXXXX02W
|
||||
Asset Tag: Not Available
|
||||
Features:
|
||||
Board is a hosting board
|
||||
Board is replaceable
|
||||
Location In Chassis: Not Available
|
||||
Chassis Handle: 0x0000
|
||||
Type: Motherboard
|
||||
Contained Object Handles: 0
|
||||
|
||||
Handle 0x0014, DMI type 3, 22 bytes
|
||||
Chassis Information
|
||||
Manufacturer: LENOVO
|
||||
Type: Notebook
|
||||
Lock: Not Present
|
||||
Version: None
|
||||
Serial Number: XXXXXXXX
|
||||
Asset Tag: No Asset Information
|
||||
Boot-up State: Unknown
|
||||
Power Supply State: Unknown
|
||||
Thermal State: Unknown
|
||||
Security Status: Unknown
|
||||
OEM Information: 0x00000000
|
||||
Height: Unspecified
|
||||
Number Of Power Cords: Unspecified
|
||||
Contained Elements: 0
|
||||
SKU Number: Not Specified
|
||||
|
||||
Handle 0x0015, DMI type 8, 9 bytes
|
||||
Port Connector Information
|
||||
Internal Reference Designator: Not Available
|
||||
Internal Connector Type: None
|
||||
External Reference Designator: USB 1
|
||||
External Connector Type: Access Bus (USB)
|
||||
Port Type: USB
|
||||
|
||||
Handle 0x0016, DMI type 8, 9 bytes
|
||||
Port Connector Information
|
||||
Internal Reference Designator: Not Available
|
||||
Internal Connector Type: None
|
||||
External Reference Designator: USB 2
|
||||
External Connector Type: Access Bus (USB)
|
||||
Port Type: USB
|
||||
|
||||
Handle 0x0017, DMI type 8, 9 bytes
|
||||
Port Connector Information
|
||||
Internal Reference Designator: Not Available
|
||||
Internal Connector Type: None
|
||||
External Reference Designator: USB 3
|
||||
External Connector Type: Access Bus (USB)
|
||||
Port Type: USB
|
||||
|
||||
Handle 0x0018, DMI type 8, 9 bytes
|
||||
Port Connector Information
|
||||
Internal Reference Designator: Not Available
|
||||
Internal Connector Type: None
|
||||
External Reference Designator: USB 4
|
||||
External Connector Type: Access Bus (USB)
|
||||
Port Type: USB
|
||||
|
||||
Handle 0x0019, DMI type 126, 9 bytes
|
||||
Inactive
|
||||
|
||||
Handle 0x001A, DMI type 126, 9 bytes
|
||||
Inactive
|
||||
|
||||
Handle 0x001B, DMI type 126, 9 bytes
|
||||
Inactive
|
||||
|
||||
Handle 0x001C, DMI type 126, 9 bytes
|
||||
Inactive
|
||||
|
||||
Handle 0x001D, DMI type 126, 9 bytes
|
||||
Inactive
|
||||
|
||||
Handle 0x001E, DMI type 8, 9 bytes
|
||||
Port Connector Information
|
||||
Internal Reference Designator: Not Available
|
||||
Internal Connector Type: None
|
||||
External Reference Designator: Ethernet
|
||||
External Connector Type: RJ-45
|
||||
Port Type: Network Port
|
||||
|
||||
Handle 0x001F, DMI type 126, 9 bytes
|
||||
Inactive
|
||||
|
||||
Handle 0x0020, DMI type 126, 9 bytes
|
||||
Inactive
|
||||
|
||||
Handle 0x0021, DMI type 8, 9 bytes
|
||||
Port Connector Information
|
||||
Internal Reference Designator: Not Available
|
||||
Internal Connector Type: None
|
||||
External Reference Designator: Hdmi1
|
||||
External Connector Type: Other
|
||||
Port Type: Video Port
|
||||
|
||||
Handle 0x0022, DMI type 126, 9 bytes
|
||||
Inactive
|
||||
|
||||
Handle 0x0023, DMI type 126, 9 bytes
|
||||
Inactive
|
||||
|
||||
Handle 0x0024, DMI type 126, 9 bytes
|
||||
Inactive
|
||||
|
||||
Handle 0x0025, DMI type 126, 9 bytes
|
||||
Inactive
|
||||
|
||||
Handle 0x0026, DMI type 8, 9 bytes
|
||||
Port Connector Information
|
||||
Internal Reference Designator: Not Available
|
||||
Internal Connector Type: None
|
||||
External Reference Designator: Headphone/Microphone Combo Jack1
|
||||
External Connector Type: Mini Jack (headphones)
|
||||
Port Type: Audio Port
|
||||
|
||||
Handle 0x0027, DMI type 126, 9 bytes
|
||||
Inactive
|
||||
|
||||
Handle 0x0028, DMI type 9, 17 bytes
|
||||
System Slot Information
|
||||
Designation: Media Card Slot
|
||||
Type: Other
|
||||
Current Usage: Available
|
||||
Length: Other
|
||||
Characteristics:
|
||||
Hot-plug devices are supported
|
||||
Bus Address: 00ff:ff:1f.7
|
||||
|
||||
Handle 0x0029, DMI type 12, 5 bytes
|
||||
System Configuration Options
|
||||
|
||||
Handle 0x002A, DMI type 13, 22 bytes
|
||||
BIOS Language Information
|
||||
Language Description Format: Abbreviated
|
||||
Installable Languages: 1
|
||||
en-US
|
||||
Currently Installed Language: en-US
|
||||
|
||||
Handle 0x002B, DMI type 22, 26 bytes
|
||||
Portable Battery
|
||||
Location: Front
|
||||
Manufacturer: SMP
|
||||
Name: 01YU911
|
||||
Design Capacity: 80400 mWh
|
||||
Design Voltage: 15360 mV
|
||||
SBDS Version: 03.01
|
||||
Maximum Error: Unknown
|
||||
SBDS Serial Number: 09E9
|
||||
SBDS Manufacture Date: 2019-12-10
|
||||
SBDS Chemistry: LiP
|
||||
OEM-specific Information: 0x00000000
|
||||
|
||||
Handle 0x002C, DMI type 126, 26 bytes
|
||||
Inactive
|
||||
|
||||
Handle 0x002D, DMI type 140, 15 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
8C 0F 2D 00 4C 45 4E 4F 56 4F 0B 09 01 01 02
|
||||
Strings:
|
||||
1.34
|
||||
1.34
|
||||
|
||||
Handle 0x002E, DMI type 133, 5 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
85 05 2E 00 01
|
||||
Strings:
|
||||
KHOIHGIUCCHHII
|
||||
|
||||
Handle 0x002F, DMI type 135, 19 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
87 13 2F 00 54 50 07 02 42 41 59 20 49 2F 4F 20
|
||||
04 00 00
|
||||
|
||||
Handle 0x0030, DMI type 130, 20 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
82 14 30 00 24 41 4D 54 01 00 01 00 01 A5 AF 02
|
||||
C0 00 00 00
|
||||
|
||||
Handle 0x0031, DMI type 131, 64 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
83 40 31 00 35 00 00 00 0C 00 00 00 00 00 0A 00
|
||||
F8 00 0E A3 00 00 00 00 09 C0 00 00 00 00 0C 00
|
||||
74 06 46 00 00 00 00 00 FE 00 BB 15 00 00 00 00
|
||||
00 00 00 00 26 00 00 00 76 50 72 6F 00 00 00 00
|
||||
|
||||
Handle 0x0032, DMI type 24, 5 bytes
|
||||
Hardware Security
|
||||
Power-On Password Status: Disabled
|
||||
Keyboard Password Status: Not Implemented
|
||||
Administrator Password Status: Disabled
|
||||
Front Panel Reset Status: Not Implemented
|
||||
|
||||
Handle 0x0033, DMI type 132, 7 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
84 07 33 00 01 C0 36
|
||||
|
||||
Handle 0x0034, DMI type 18, 23 bytes
|
||||
32-bit Memory Error Information
|
||||
Type: OK
|
||||
Granularity: Unknown
|
||||
Operation: Unknown
|
||||
Vendor Syndrome: Unknown
|
||||
Memory Array Address: Unknown
|
||||
Device Address: Unknown
|
||||
Resolution: Unknown
|
||||
|
||||
Handle 0x0035, DMI type 21, 7 bytes
|
||||
Built-in Pointing Device
|
||||
Type: Track Point
|
||||
Interface: PS/2
|
||||
Buttons: 3
|
||||
|
||||
Handle 0x0036, DMI type 21, 7 bytes
|
||||
Built-in Pointing Device
|
||||
Type: Touch Pad
|
||||
Interface: PS/2
|
||||
Buttons: 2
|
||||
|
||||
Handle 0x0037, DMI type 131, 22 bytes
|
||||
ThinkVantage Technologies
|
||||
Version: 1
|
||||
Diagnostics: No
|
||||
|
||||
Handle 0x0038, DMI type 136, 6 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
88 06 38 00 5A 5A
|
||||
|
||||
Handle 0x0039, DMI type 15, 31 bytes
|
||||
System Event Log
|
||||
Area Length: 786 bytes
|
||||
Header Start Offset: 0x0000
|
||||
Header Length: 16 bytes
|
||||
Data Start Offset: 0x0010
|
||||
Access Method: General-purpose non-volatile data functions
|
||||
Access Address: 0x00F0
|
||||
Status: Valid, Not Full
|
||||
Change Token: 0x00000030
|
||||
Header Format: Type 1
|
||||
Supported Log Type Descriptors: 4
|
||||
Descriptor 1: POST error
|
||||
Data Format 1: POST results bitmap
|
||||
Descriptor 2: PCI system error
|
||||
Data Format 2: None
|
||||
Descriptor 3: System reconfigured
|
||||
Data Format 3: None
|
||||
Descriptor 4: Log area reset/cleared
|
||||
Data Format 4: None
|
||||
|
||||
Handle 0x003A, DMI type 140, 19 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
8C 13 3A 00 4C 45 4E 4F 56 4F 0B 04 01 B2 00 4D
|
||||
53 20 00
|
||||
|
||||
Handle 0x003B, DMI type 140, 19 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
8C 13 3B 00 4C 45 4E 4F 56 4F 0B 05 01 07 00 00
|
||||
00 00 00
|
||||
|
||||
Handle 0x003C, DMI type 140, 23 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
8C 17 3C 00 4C 45 4E 4F 56 4F 0B 06 01 CB 06 51
|
||||
74 01 60 00 00 00 00
|
||||
|
||||
Handle 0x003D, DMI type 14, 8 bytes
|
||||
Group Associations
|
||||
Name: $MEI
|
||||
Items: 1
|
||||
0x0000 (OEM-specific)
|
||||
|
||||
Handle 0x003E, DMI type 219, 106 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
DB 6A 3E 00 01 04 01 45 02 00 94 06 81 10 89 30
|
||||
00 00 00 04 40 00 00 01 1F 00 00 C9 0A 40 C4 02
|
||||
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
|
||||
FF FF FF FF FF FF FF FF 03 00 00 00 80 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00
|
||||
Strings:
|
||||
MEI1
|
||||
MEI2
|
||||
MEI3
|
||||
MEI4
|
||||
|
||||
Handle 0x003F, DMI type 140, 15 bytes
|
||||
ThinkPad Embedded Controller Program
|
||||
Version ID: N2OHT36W
|
||||
Release Date: 05/07/2020
|
||||
|
||||
Handle 0x0040, DMI type 140, 43 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
8C 2B 40 00 4C 45 4E 4F 56 4F 0B 08 01 03 21 08
|
||||
05 18 39 03 21 05 19 18 04 03 21 05 19 17 56 03
|
||||
21 05 19 17 54 03 21 05 19 17 48
|
||||
|
||||
Handle 0x0041, DMI type 135, 18 bytes
|
||||
OEM-specific Type
|
||||
Header and Data:
|
||||
87 12 41 00 54 50 07 01 01 00 00 00 03 00 00 00
|
||||
03 00
|
||||
|
||||
Handle 0xFEFF, DMI type 127, 4 bytes
|
||||
End Of Table
|
||||
`
|
||||
|
||||
var testOutput = `{
|
||||
"32-bit Memory Error Information": {
|
||||
"Device Address": "Unknown",
|
||||
"Granularity": "Unknown",
|
||||
"Memory Array Address": "Unknown",
|
||||
"Operation": "Unknown",
|
||||
"Resolution": "Unknown",
|
||||
"Type": "OK",
|
||||
"Vendor Syndrome": "Unknown"
|
||||
},
|
||||
"BIOS Information": {
|
||||
"Address": "0xE0000",
|
||||
"BIOS Revision": "1.34",
|
||||
"Characteristics:": [
|
||||
"PCI is supported",
|
||||
"PNP is supported",
|
||||
"BIOS is upgradeable",
|
||||
"BIOS shadowing is allowed",
|
||||
"Boot from CD is supported",
|
||||
"Selectable boot is supported",
|
||||
"EDD is supported",
|
||||
"3.5\"/720 kB floppy services are supported (int 13h)",
|
||||
"Print screen service is supported (int 5h)",
|
||||
"8042 keyboard services are supported (int 9h)",
|
||||
"Serial services are supported (int 14h)",
|
||||
"Printer services are supported (int 17h)",
|
||||
"CGA/mono video services are supported (int 10h)",
|
||||
"ACPI is supported",
|
||||
"USB legacy is supported",
|
||||
"BIOS boot specification is supported",
|
||||
"Targeted content distribution is supported",
|
||||
"UEFI is supported"
|
||||
],
|
||||
"Firmware Revision": "1.23",
|
||||
"ROM Size": "32 MB",
|
||||
"Release Date": "08/06/2020",
|
||||
"Runtime Size": "128 kB",
|
||||
"Vendor": "LENOVO",
|
||||
"Version": "N2OET47W (1.34 )"
|
||||
},
|
||||
"BIOS Language Information": {
|
||||
"Currently Installed Language": "en-US",
|
||||
"Installable Languages": [
|
||||
"en-US"
|
||||
],
|
||||
"Language Description Format": "Abbreviated"
|
||||
},
|
||||
"Base Board Information": {
|
||||
"Asset Tag": "Not Available",
|
||||
"Chassis Handle": "0x0000",
|
||||
"Contained Object Handles": "0",
|
||||
"Features:": [
|
||||
"Board is a hosting board",
|
||||
"Board is replaceable"
|
||||
],
|
||||
"Location In Chassis": "Not Available",
|
||||
"Manufacturer": "LENOVO",
|
||||
"Product Name": "20QTS00Y00",
|
||||
"Serial Number": "XXXXXXXX02W",
|
||||
"Type": "Motherboard",
|
||||
"Version": "SDK0T08861 WIN"
|
||||
},
|
||||
"Built-in Pointing Device": {
|
||||
"Buttons": "2",
|
||||
"Interface": "PS/2",
|
||||
"Type": "Touch Pad"
|
||||
},
|
||||
"Cache Information": {
|
||||
"Associativity": "16-way Set-associative",
|
||||
"Configuration": "Enabled, Not Socketed, Level 3",
|
||||
"Error Correction Type": "Multi-bit ECC",
|
||||
"Installed SRAM Type": "Synchronous",
|
||||
"Installed Size": "16384 kB",
|
||||
"Location": "Internal",
|
||||
"Maximum Size": "16384 kB",
|
||||
"Operational Mode": "Write Back",
|
||||
"Socket Designation": "L3 Cache",
|
||||
"Speed": "Unknown",
|
||||
"Supported SRAM Types:": [
|
||||
"Synchronous"
|
||||
],
|
||||
"System Type": "Unified"
|
||||
},
|
||||
"Chassis Information": {
|
||||
"Asset Tag": "No Asset Information",
|
||||
"Boot-up State": "Unknown",
|
||||
"Contained Elements": "0",
|
||||
"Height": "Unspecified",
|
||||
"Lock": "Not Present",
|
||||
"Manufacturer": "LENOVO",
|
||||
"Number Of Power Cords": "Unspecified",
|
||||
"OEM Information": "0x00000000",
|
||||
"Power Supply State": "Unknown",
|
||||
"SKU Number": "Not Specified",
|
||||
"Security Status": "Unknown",
|
||||
"Serial Number": "XXXXXXXX",
|
||||
"Thermal State": "Unknown",
|
||||
"Type": "Notebook",
|
||||
"Version": "None"
|
||||
},
|
||||
"Group Associations": {
|
||||
"Items": [
|
||||
"0x0000 (OEM-specific)"
|
||||
],
|
||||
"Name": "$MEI"
|
||||
},
|
||||
"Hardware Security": {
|
||||
"Administrator Password Status": "Disabled",
|
||||
"Front Panel Reset Status": "Not Implemented",
|
||||
"Keyboard Password Status": "Not Implemented",
|
||||
"Power-On Password Status": "Disabled"
|
||||
},
|
||||
"Inactive": {},
|
||||
"Memory Array Mapped Address": {
|
||||
"Ending Address": "0x00FFFFFFFFF",
|
||||
"Partition Width": "2",
|
||||
"Physical Array Handle": "0x0003",
|
||||
"Range Size": "64 GB",
|
||||
"Starting Address": "0x00000000000"
|
||||
},
|
||||
"Memory Device": {
|
||||
"Array Handle": "0x0003",
|
||||
"Asset Tag": "None",
|
||||
"Bank Locator": "BANK 2",
|
||||
"Configured Memory Speed": "2667 MT/s",
|
||||
"Configured Voltage": "1.2 V",
|
||||
"Data Width": "64 bits",
|
||||
"Error Information Handle": "Not Provided",
|
||||
"Form Factor": "SODIMM",
|
||||
"Locator": "ChannelB-DIMM0",
|
||||
"Manufacturer": "Micron",
|
||||
"Maximum Voltage": "Unknown",
|
||||
"Minimum Voltage": "Unknown",
|
||||
"Part Number": "16ATF4G64HZ-2G6B2",
|
||||
"Rank": "2",
|
||||
"Serial Number": "XXXXXXXX",
|
||||
"Set": "None",
|
||||
"Size": "32 GB",
|
||||
"Speed": "2667 MT/s",
|
||||
"Total Width": "64 bits",
|
||||
"Type": "DDR4",
|
||||
"Type Detail": "Synchronous"
|
||||
},
|
||||
"Physical Memory Array": {
|
||||
"Error Correction Type": "None",
|
||||
"Error Information Handle": "Not Provided",
|
||||
"Location": "System Board Or Motherboard",
|
||||
"Maximum Capacity": "64 GB",
|
||||
"Number Of Devices": "4",
|
||||
"Use": "System Memory"
|
||||
},
|
||||
"Port Connector Information": {
|
||||
"External Connector Type": "Mini Jack (headphones)",
|
||||
"External Reference Designator": "Headphone/Microphone Combo Jack1",
|
||||
"Internal Connector Type": "None",
|
||||
"Internal Reference Designator": "Not Available",
|
||||
"Port Type": "Audio Port"
|
||||
},
|
||||
"Portable Battery": {
|
||||
"Design Capacity": "80400 mWh",
|
||||
"Design Voltage": "15360 mV",
|
||||
"Location": "Front",
|
||||
"Manufacturer": "SMP",
|
||||
"Maximum Error": "Unknown",
|
||||
"Name": "01YU911",
|
||||
"OEM-specific Information": "0x00000000",
|
||||
"SBDS Chemistry": "LiP",
|
||||
"SBDS Manufacture Date": "2019-12-10",
|
||||
"SBDS Serial Number": "09E9",
|
||||
"SBDS Version": "03.01"
|
||||
},
|
||||
"Processor Information": {
|
||||
"Asset Tag": "None",
|
||||
"Characteristics:": [
|
||||
"64-bit capable",
|
||||
"Multi-Core",
|
||||
"Hardware Thread",
|
||||
"Execute Protection",
|
||||
"Enhanced Virtualization",
|
||||
"Power/Performance Control"
|
||||
],
|
||||
"Core Count": "8",
|
||||
"Core Enabled": "8",
|
||||
"Current Speed": "2300 MHz",
|
||||
"External Clock": "100 MHz",
|
||||
"Family": "Core i9",
|
||||
"Flags:": [
|
||||
"FPU (Floating-point unit on-chip)",
|
||||
"VME (Virtual mode extension)",
|
||||
"DE (Debugging extension)",
|
||||
"PSE (Page size extension)",
|
||||
"TSC (Time stamp counter)",
|
||||
"MSR (Model specific registers)",
|
||||
"PAE (Physical address extension)",
|
||||
"MCE (Machine check exception)",
|
||||
"CX8 (CMPXCHG8 instruction supported)",
|
||||
"APIC (On-chip APIC hardware supported)",
|
||||
"SEP (Fast system call)",
|
||||
"MTRR (Memory type range registers)",
|
||||
"PGE (Page global enable)",
|
||||
"MCA (Machine check architecture)",
|
||||
"CMOV (Conditional move instruction supported)",
|
||||
"PAT (Page attribute table)",
|
||||
"PSE-36 (36-bit page size extension)",
|
||||
"CLFSH (CLFLUSH instruction supported)",
|
||||
"DS (Debug store)",
|
||||
"ACPI (ACPI supported)",
|
||||
"MMX (MMX technology supported)",
|
||||
"FXSR (FXSAVE and FXSTOR instructions supported)",
|
||||
"SSE (Streaming SIMD extensions)",
|
||||
"SSE2 (Streaming SIMD extensions 2)",
|
||||
"SS (Self-snoop)",
|
||||
"HTT (Multi-threading)",
|
||||
"TM (Thermal monitor supported)",
|
||||
"PBE (Pending break enabled)"
|
||||
],
|
||||
"ID": "ED 06 09 00 FF FB EB BF",
|
||||
"L1 Cache Handle": "0x000D",
|
||||
"L2 Cache Handle": "0x000E",
|
||||
"L3 Cache Handle": "0x000F",
|
||||
"Manufacturer": "Intel(R) Corporation",
|
||||
"Max Speed": "2300 MHz",
|
||||
"Part Number": "None",
|
||||
"Serial Number": "None",
|
||||
"Signature": "Type 0, Family 6, Model 158, Stepping 13",
|
||||
"Socket Designation": "U3E1",
|
||||
"Status": "Populated, Enabled",
|
||||
"Thread Count": "16",
|
||||
"Type": "Central Processor",
|
||||
"Upgrade": "Socket BGA1440",
|
||||
"Version": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
|
||||
"Voltage": "0.8 V"
|
||||
},
|
||||
"System Configuration Options": {},
|
||||
"System Event Log": {
|
||||
"Access Address": "0x00F0",
|
||||
"Access Method": "General-purpose non-volatile data functions",
|
||||
"Area Length": "786 bytes",
|
||||
"Change Token": "0x00000030",
|
||||
"Data Format 1": "POST results bitmap",
|
||||
"Data Format 2": "None",
|
||||
"Data Format 3": "None",
|
||||
"Data Format 4": "None",
|
||||
"Data Start Offset": "0x0010",
|
||||
"Descriptor 1": "POST error",
|
||||
"Descriptor 2": "PCI system error",
|
||||
"Descriptor 3": "System reconfigured",
|
||||
"Descriptor 4": "Log area reset/cleared",
|
||||
"Header Format": "Type 1",
|
||||
"Header Length": "16 bytes",
|
||||
"Header Start Offset": "0x0000",
|
||||
"Status": "Valid, Not Full",
|
||||
"Supported Log Type Descriptors": "4"
|
||||
},
|
||||
"System Information": {
|
||||
"Family": "ThinkPad P1 Gen 2",
|
||||
"Manufacturer": "LENOVO",
|
||||
"Product Name": "20QTS00Y00",
|
||||
"SKU Number": "LENOVO_MT_20QT_BU_Think_FM_ThinkPad P1 Gen 2",
|
||||
"Serial Number": "XXXXXXXX",
|
||||
"UUID": "069a9e4c-2eec-11b2-a85c-e9c6a8e86998",
|
||||
"Version": "ThinkPad P1 Gen 2",
|
||||
"Wake-up Type": "Power Switch"
|
||||
},
|
||||
"System Slot Information": {
|
||||
"Bus Address": "00ff:ff:1f.7",
|
||||
"Characteristics:": [
|
||||
"Hot-plug devices are supported"
|
||||
],
|
||||
"Current Usage": "Available",
|
||||
"Designation": "Media Card Slot",
|
||||
"Length": "Other",
|
||||
"Type": "Other"
|
||||
},
|
||||
"ThinkPad Embedded Controller Program": {
|
||||
"Release Date": "05/07/2020",
|
||||
"Version ID": "N2OHT36W"
|
||||
},
|
||||
"ThinkVantage Technologies": {
|
||||
"Diagnostics": "No",
|
||||
"Version": "1"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
output := dmiOutputToMap(bytes.NewBufferString(testInput))
|
||||
outputBuffer := bytes.NewBuffer(nil)
|
||||
enc := json.NewEncoder(outputBuffer)
|
||||
enc.SetIndent("", " ")
|
||||
if err := enc.Encode(output); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, testOutput, outputBuffer.String())
|
||||
}
|
@@ -31,6 +31,7 @@ func init() {
|
||||
|
||||
type Interface interface {
|
||||
MachineInventory() MachineInventoryController
|
||||
MachineRegistration() MachineRegistrationController
|
||||
ManagedOSImage() ManagedOSImageController
|
||||
}
|
||||
|
||||
@@ -47,6 +48,9 @@ type version struct {
|
||||
func (c *version) MachineInventory() MachineInventoryController {
|
||||
return NewMachineInventoryController(schema.GroupVersionKind{Group: "rancheros.cattle.io", Version: "v1", Kind: "MachineInventory"}, "machineinventories", true, c.controllerFactory)
|
||||
}
|
||||
func (c *version) MachineRegistration() MachineRegistrationController {
|
||||
return NewMachineRegistrationController(schema.GroupVersionKind{Group: "rancheros.cattle.io", Version: "v1", Kind: "MachineRegistration"}, "machineregistrations", true, c.controllerFactory)
|
||||
}
|
||||
func (c *version) ManagedOSImage() ManagedOSImageController {
|
||||
return NewManagedOSImageController(schema.GroupVersionKind{Group: "rancheros.cattle.io", Version: "v1", Kind: "ManagedOSImage"}, "managedosimages", true, c.controllerFactory)
|
||||
}
|
||||
|
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
Copyright 2021 Rancher Labs, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by main. DO NOT EDIT.
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/rancher/lasso/pkg/client"
|
||||
"github.com/rancher/lasso/pkg/controller"
|
||||
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
||||
"github.com/rancher/wrangler/pkg/apply"
|
||||
"github.com/rancher/wrangler/pkg/condition"
|
||||
"github.com/rancher/wrangler/pkg/generic"
|
||||
"github.com/rancher/wrangler/pkg/kv"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
type MachineRegistrationHandler func(string, *v1.MachineRegistration) (*v1.MachineRegistration, error)
|
||||
|
||||
type MachineRegistrationController interface {
|
||||
generic.ControllerMeta
|
||||
MachineRegistrationClient
|
||||
|
||||
OnChange(ctx context.Context, name string, sync MachineRegistrationHandler)
|
||||
OnRemove(ctx context.Context, name string, sync MachineRegistrationHandler)
|
||||
Enqueue(namespace, name string)
|
||||
EnqueueAfter(namespace, name string, duration time.Duration)
|
||||
|
||||
Cache() MachineRegistrationCache
|
||||
}
|
||||
|
||||
type MachineRegistrationClient interface {
|
||||
Create(*v1.MachineRegistration) (*v1.MachineRegistration, error)
|
||||
Update(*v1.MachineRegistration) (*v1.MachineRegistration, error)
|
||||
UpdateStatus(*v1.MachineRegistration) (*v1.MachineRegistration, error)
|
||||
Delete(namespace, name string, options *metav1.DeleteOptions) error
|
||||
Get(namespace, name string, options metav1.GetOptions) (*v1.MachineRegistration, error)
|
||||
List(namespace string, opts metav1.ListOptions) (*v1.MachineRegistrationList, error)
|
||||
Watch(namespace string, opts metav1.ListOptions) (watch.Interface, error)
|
||||
Patch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.MachineRegistration, err error)
|
||||
}
|
||||
|
||||
type MachineRegistrationCache interface {
|
||||
Get(namespace, name string) (*v1.MachineRegistration, error)
|
||||
List(namespace string, selector labels.Selector) ([]*v1.MachineRegistration, error)
|
||||
|
||||
AddIndexer(indexName string, indexer MachineRegistrationIndexer)
|
||||
GetByIndex(indexName, key string) ([]*v1.MachineRegistration, error)
|
||||
}
|
||||
|
||||
type MachineRegistrationIndexer func(obj *v1.MachineRegistration) ([]string, error)
|
||||
|
||||
type machineRegistrationController struct {
|
||||
controller controller.SharedController
|
||||
client *client.Client
|
||||
gvk schema.GroupVersionKind
|
||||
groupResource schema.GroupResource
|
||||
}
|
||||
|
||||
func NewMachineRegistrationController(gvk schema.GroupVersionKind, resource string, namespaced bool, controller controller.SharedControllerFactory) MachineRegistrationController {
|
||||
c := controller.ForResourceKind(gvk.GroupVersion().WithResource(resource), gvk.Kind, namespaced)
|
||||
return &machineRegistrationController{
|
||||
controller: c,
|
||||
client: c.Client(),
|
||||
gvk: gvk,
|
||||
groupResource: schema.GroupResource{
|
||||
Group: gvk.Group,
|
||||
Resource: resource,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func FromMachineRegistrationHandlerToHandler(sync MachineRegistrationHandler) generic.Handler {
|
||||
return func(key string, obj runtime.Object) (ret runtime.Object, err error) {
|
||||
var v *v1.MachineRegistration
|
||||
if obj == nil {
|
||||
v, err = sync(key, nil)
|
||||
} else {
|
||||
v, err = sync(key, obj.(*v1.MachineRegistration))
|
||||
}
|
||||
if v == nil {
|
||||
return nil, err
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) Updater() generic.Updater {
|
||||
return func(obj runtime.Object) (runtime.Object, error) {
|
||||
newObj, err := c.Update(obj.(*v1.MachineRegistration))
|
||||
if newObj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return newObj, err
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateMachineRegistrationDeepCopyOnChange(client MachineRegistrationClient, obj *v1.MachineRegistration, handler func(obj *v1.MachineRegistration) (*v1.MachineRegistration, error)) (*v1.MachineRegistration, error) {
|
||||
if obj == nil {
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
copyObj := obj.DeepCopy()
|
||||
newObj, err := handler(copyObj)
|
||||
if newObj != nil {
|
||||
copyObj = newObj
|
||||
}
|
||||
if obj.ResourceVersion == copyObj.ResourceVersion && !equality.Semantic.DeepEqual(obj, copyObj) {
|
||||
return client.Update(copyObj)
|
||||
}
|
||||
|
||||
return copyObj, err
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) AddGenericHandler(ctx context.Context, name string, handler generic.Handler) {
|
||||
c.controller.RegisterHandler(ctx, name, controller.SharedControllerHandlerFunc(handler))
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) AddGenericRemoveHandler(ctx context.Context, name string, handler generic.Handler) {
|
||||
c.AddGenericHandler(ctx, name, generic.NewRemoveHandler(name, c.Updater(), handler))
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) OnChange(ctx context.Context, name string, sync MachineRegistrationHandler) {
|
||||
c.AddGenericHandler(ctx, name, FromMachineRegistrationHandlerToHandler(sync))
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) OnRemove(ctx context.Context, name string, sync MachineRegistrationHandler) {
|
||||
c.AddGenericHandler(ctx, name, generic.NewRemoveHandler(name, c.Updater(), FromMachineRegistrationHandlerToHandler(sync)))
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) Enqueue(namespace, name string) {
|
||||
c.controller.Enqueue(namespace, name)
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) EnqueueAfter(namespace, name string, duration time.Duration) {
|
||||
c.controller.EnqueueAfter(namespace, name, duration)
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) Informer() cache.SharedIndexInformer {
|
||||
return c.controller.Informer()
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) GroupVersionKind() schema.GroupVersionKind {
|
||||
return c.gvk
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) Cache() MachineRegistrationCache {
|
||||
return &machineRegistrationCache{
|
||||
indexer: c.Informer().GetIndexer(),
|
||||
resource: c.groupResource,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) Create(obj *v1.MachineRegistration) (*v1.MachineRegistration, error) {
|
||||
result := &v1.MachineRegistration{}
|
||||
return result, c.client.Create(context.TODO(), obj.Namespace, obj, result, metav1.CreateOptions{})
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) Update(obj *v1.MachineRegistration) (*v1.MachineRegistration, error) {
|
||||
result := &v1.MachineRegistration{}
|
||||
return result, c.client.Update(context.TODO(), obj.Namespace, obj, result, metav1.UpdateOptions{})
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) UpdateStatus(obj *v1.MachineRegistration) (*v1.MachineRegistration, error) {
|
||||
result := &v1.MachineRegistration{}
|
||||
return result, c.client.UpdateStatus(context.TODO(), obj.Namespace, obj, result, metav1.UpdateOptions{})
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) Delete(namespace, name string, options *metav1.DeleteOptions) error {
|
||||
if options == nil {
|
||||
options = &metav1.DeleteOptions{}
|
||||
}
|
||||
return c.client.Delete(context.TODO(), namespace, name, *options)
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) Get(namespace, name string, options metav1.GetOptions) (*v1.MachineRegistration, error) {
|
||||
result := &v1.MachineRegistration{}
|
||||
return result, c.client.Get(context.TODO(), namespace, name, result, options)
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) List(namespace string, opts metav1.ListOptions) (*v1.MachineRegistrationList, error) {
|
||||
result := &v1.MachineRegistrationList{}
|
||||
return result, c.client.List(context.TODO(), namespace, result, opts)
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) Watch(namespace string, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return c.client.Watch(context.TODO(), namespace, opts)
|
||||
}
|
||||
|
||||
func (c *machineRegistrationController) Patch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (*v1.MachineRegistration, error) {
|
||||
result := &v1.MachineRegistration{}
|
||||
return result, c.client.Patch(context.TODO(), namespace, name, pt, data, result, metav1.PatchOptions{}, subresources...)
|
||||
}
|
||||
|
||||
type machineRegistrationCache struct {
|
||||
indexer cache.Indexer
|
||||
resource schema.GroupResource
|
||||
}
|
||||
|
||||
func (c *machineRegistrationCache) Get(namespace, name string) (*v1.MachineRegistration, error) {
|
||||
obj, exists, err := c.indexer.GetByKey(namespace + "/" + name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
return nil, errors.NewNotFound(c.resource, name)
|
||||
}
|
||||
return obj.(*v1.MachineRegistration), nil
|
||||
}
|
||||
|
||||
func (c *machineRegistrationCache) List(namespace string, selector labels.Selector) (ret []*v1.MachineRegistration, err error) {
|
||||
|
||||
err = cache.ListAllByNamespace(c.indexer, namespace, selector, func(m interface{}) {
|
||||
ret = append(ret, m.(*v1.MachineRegistration))
|
||||
})
|
||||
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (c *machineRegistrationCache) AddIndexer(indexName string, indexer MachineRegistrationIndexer) {
|
||||
utilruntime.Must(c.indexer.AddIndexers(map[string]cache.IndexFunc{
|
||||
indexName: func(obj interface{}) (strings []string, e error) {
|
||||
return indexer(obj.(*v1.MachineRegistration))
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
func (c *machineRegistrationCache) GetByIndex(indexName, key string) (result []*v1.MachineRegistration, err error) {
|
||||
objs, err := c.indexer.ByIndex(indexName, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = make([]*v1.MachineRegistration, 0, len(objs))
|
||||
for _, obj := range objs {
|
||||
result = append(result, obj.(*v1.MachineRegistration))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type MachineRegistrationStatusHandler func(obj *v1.MachineRegistration, status v1.MachineRegistrationStatus) (v1.MachineRegistrationStatus, error)
|
||||
|
||||
type MachineRegistrationGeneratingHandler func(obj *v1.MachineRegistration, status v1.MachineRegistrationStatus) ([]runtime.Object, v1.MachineRegistrationStatus, error)
|
||||
|
||||
func RegisterMachineRegistrationStatusHandler(ctx context.Context, controller MachineRegistrationController, condition condition.Cond, name string, handler MachineRegistrationStatusHandler) {
|
||||
statusHandler := &machineRegistrationStatusHandler{
|
||||
client: controller,
|
||||
condition: condition,
|
||||
handler: handler,
|
||||
}
|
||||
controller.AddGenericHandler(ctx, name, FromMachineRegistrationHandlerToHandler(statusHandler.sync))
|
||||
}
|
||||
|
||||
func RegisterMachineRegistrationGeneratingHandler(ctx context.Context, controller MachineRegistrationController, apply apply.Apply,
|
||||
condition condition.Cond, name string, handler MachineRegistrationGeneratingHandler, opts *generic.GeneratingHandlerOptions) {
|
||||
statusHandler := &machineRegistrationGeneratingHandler{
|
||||
MachineRegistrationGeneratingHandler: handler,
|
||||
apply: apply,
|
||||
name: name,
|
||||
gvk: controller.GroupVersionKind(),
|
||||
}
|
||||
if opts != nil {
|
||||
statusHandler.opts = *opts
|
||||
}
|
||||
controller.OnChange(ctx, name, statusHandler.Remove)
|
||||
RegisterMachineRegistrationStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
||||
}
|
||||
|
||||
type machineRegistrationStatusHandler struct {
|
||||
client MachineRegistrationClient
|
||||
condition condition.Cond
|
||||
handler MachineRegistrationStatusHandler
|
||||
}
|
||||
|
||||
func (a *machineRegistrationStatusHandler) sync(key string, obj *v1.MachineRegistration) (*v1.MachineRegistration, error) {
|
||||
if obj == nil {
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
origStatus := obj.Status.DeepCopy()
|
||||
obj = obj.DeepCopy()
|
||||
newStatus, err := a.handler(obj, obj.Status)
|
||||
if err != nil {
|
||||
// Revert to old status on error
|
||||
newStatus = *origStatus.DeepCopy()
|
||||
}
|
||||
|
||||
if a.condition != "" {
|
||||
if errors.IsConflict(err) {
|
||||
a.condition.SetError(&newStatus, "", nil)
|
||||
} else {
|
||||
a.condition.SetError(&newStatus, "", err)
|
||||
}
|
||||
}
|
||||
if !equality.Semantic.DeepEqual(origStatus, &newStatus) {
|
||||
if a.condition != "" {
|
||||
// Since status has changed, update the lastUpdatedTime
|
||||
a.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))
|
||||
}
|
||||
|
||||
var newErr error
|
||||
obj.Status = newStatus
|
||||
newObj, newErr := a.client.UpdateStatus(obj)
|
||||
if err == nil {
|
||||
err = newErr
|
||||
}
|
||||
if newErr == nil {
|
||||
obj = newObj
|
||||
}
|
||||
}
|
||||
return obj, err
|
||||
}
|
||||
|
||||
type machineRegistrationGeneratingHandler struct {
|
||||
MachineRegistrationGeneratingHandler
|
||||
apply apply.Apply
|
||||
opts generic.GeneratingHandlerOptions
|
||||
gvk schema.GroupVersionKind
|
||||
name string
|
||||
}
|
||||
|
||||
func (a *machineRegistrationGeneratingHandler) Remove(key string, obj *v1.MachineRegistration) (*v1.MachineRegistration, error) {
|
||||
if obj != nil {
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
obj = &v1.MachineRegistration{}
|
||||
obj.Namespace, obj.Name = kv.RSplit(key, "/")
|
||||
obj.SetGroupVersionKind(a.gvk)
|
||||
|
||||
return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||
WithOwner(obj).
|
||||
WithSetID(a.name).
|
||||
ApplyObjects()
|
||||
}
|
||||
|
||||
func (a *machineRegistrationGeneratingHandler) Handle(obj *v1.MachineRegistration, status v1.MachineRegistrationStatus) (v1.MachineRegistrationStatus, error) {
|
||||
if !obj.DeletionTimestamp.IsZero() {
|
||||
return status, nil
|
||||
}
|
||||
|
||||
objs, newStatus, err := a.MachineRegistrationGeneratingHandler(obj, status)
|
||||
if err != nil {
|
||||
return newStatus, err
|
||||
}
|
||||
|
||||
return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||
WithOwner(obj).
|
||||
WithSetID(a.name).
|
||||
ApplyObjects(objs...)
|
||||
}
|
@@ -11,16 +11,24 @@ import (
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func Run(automatic bool, configFile string) error {
|
||||
func Run(automatic bool, configFile string, powerOff bool, silent bool) error {
|
||||
cfg, err := config.ReadConfig(configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if powerOff {
|
||||
cfg.RancherOS.Install.PowerOff = true
|
||||
}
|
||||
|
||||
if automatic && !cfg.RancherOS.Install.Automatic {
|
||||
return nil
|
||||
}
|
||||
|
||||
if silent {
|
||||
cfg.RancherOS.Install.Automatic = true
|
||||
}
|
||||
|
||||
err = Ask(&cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/rancher/os2/pkg/clients"
|
||||
"github.com/rancher/os2/pkg/controllers/inventory"
|
||||
"github.com/rancher/os2/pkg/controllers/managedos"
|
||||
"github.com/rancher/os2/pkg/controllers/registration"
|
||||
"github.com/rancher/os2/pkg/server"
|
||||
"github.com/rancher/steve/pkg/aggregation"
|
||||
"github.com/rancher/wrangler/pkg/crd"
|
||||
@@ -39,6 +40,10 @@ func Run(ctx context.Context, namespace string) error {
|
||||
SchemaObject: v1.MachineInventory{},
|
||||
Status: true,
|
||||
},
|
||||
crd.CRD{
|
||||
SchemaObject: v1.MachineRegistration{},
|
||||
Status: true,
|
||||
},
|
||||
).BatchWait()
|
||||
if err != nil {
|
||||
logrus.Fatalf("Failed to create CRDs: %v", err)
|
||||
@@ -46,6 +51,7 @@ func Run(ctx context.Context, namespace string) error {
|
||||
|
||||
managedos.Register(ctx, clients)
|
||||
inventory.Register(ctx, clients)
|
||||
registration.Register(ctx, clients)
|
||||
|
||||
aggregation.Watch(ctx, clients.Core.Secret(), namespace, "rancheros-operator", server.New(clients))
|
||||
return clients.Start(ctx)
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
@@ -29,7 +30,7 @@ func (i *InventoryServer) cacerts(rw http.ResponseWriter, req *http.Request) {
|
||||
|
||||
if authorization != "" && nonce != "" {
|
||||
crt, err := i.secretCache.GetByIndex(tokenHash, authorization)
|
||||
if err == nil && len(crt) >= 0 {
|
||||
if err == nil && len(crt) > 0 {
|
||||
digest := hmac.New(sha512.New, crt[0].Data[tokenKey])
|
||||
digest.Write([]byte(nonce))
|
||||
digest.Write([]byte{0})
|
||||
@@ -37,6 +38,14 @@ func (i *InventoryServer) cacerts(rw http.ResponseWriter, req *http.Request) {
|
||||
digest.Write([]byte{0})
|
||||
hash := digest.Sum(nil)
|
||||
rw.Header().Set("X-Cattle-Hash", base64.StdEncoding.EncodeToString(hash))
|
||||
} else if machines, err := i.machineCache.GetByIndex(tokenHash, authorization); len(machines) == 1 && err == nil {
|
||||
digest := hmac.New(sha512.New, []byte(machines[0].Spec.TPMHash))
|
||||
digest.Write([]byte(nonce))
|
||||
digest.Write([]byte{0})
|
||||
digest.Write(bytes)
|
||||
digest.Write([]byte{0})
|
||||
hash := digest.Sum(nil)
|
||||
rw.Header().Set("X-Cattle-Hash", base64.StdEncoding.EncodeToString(hash))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,5 +59,22 @@ func (i *InventoryServer) cacert() string {
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if setting.Value == "" {
|
||||
setting, err = i.settingCache.Get("internal-cacerts")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return setting.Value
|
||||
}
|
||||
|
||||
func (i *InventoryServer) serverURL() (string, error) {
|
||||
setting, err := i.settingCache.Get("server-url")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if setting.Value == "" {
|
||||
return "", fmt.Errorf("server-url is not set")
|
||||
}
|
||||
return setting.Value, nil
|
||||
}
|
||||
|
182
pkg/server/register.go
Normal file
182
pkg/server/register.go
Normal file
@@ -0,0 +1,182 @@
|
||||
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)
|
||||
}
|
81
pkg/server/register_test.go
Normal file
81
pkg/server/register_test.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func TestBuildName(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"level1A": map[string]interface{}{
|
||||
"level2A": "level2AValue",
|
||||
"level2B": map[string]interface{}{
|
||||
"level3A": "level3AValue",
|
||||
},
|
||||
},
|
||||
"level1B": "level1BValue",
|
||||
}
|
||||
|
||||
testCase := []struct {
|
||||
Format string
|
||||
Output string
|
||||
}{
|
||||
{
|
||||
Format: "${level1B}",
|
||||
Output: "level1bvalue",
|
||||
},
|
||||
{
|
||||
Format: "${level1B",
|
||||
Output: "m-level1b",
|
||||
},
|
||||
{
|
||||
Format: "a${level1B",
|
||||
Output: "a-level1b",
|
||||
},
|
||||
{
|
||||
Format: "${}",
|
||||
Output: "m",
|
||||
},
|
||||
{
|
||||
Format: "${",
|
||||
Output: "m-",
|
||||
},
|
||||
{
|
||||
Format: "a${",
|
||||
Output: "a-",
|
||||
},
|
||||
{
|
||||
Format: "${level1A}",
|
||||
Output: "m",
|
||||
},
|
||||
{
|
||||
Format: "a${level1A}c",
|
||||
Output: "ac",
|
||||
},
|
||||
{
|
||||
Format: "a${level1A}",
|
||||
Output: "a",
|
||||
},
|
||||
{
|
||||
Format: "${level1A}c",
|
||||
Output: "c",
|
||||
},
|
||||
{
|
||||
Format: "a${level1A/level2A}c",
|
||||
Output: "alevel2avaluec",
|
||||
},
|
||||
{
|
||||
Format: "a${level1A/level2B/level3A}c",
|
||||
Output: "alevel3avaluec",
|
||||
},
|
||||
{
|
||||
Format: "a${level1A/level2B/level3A}c${level1B}",
|
||||
Output: "alevel3avalueclevel1bvalue",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCase {
|
||||
assert.Equal(t, testCase.Output, buildName(data, testCase.Format))
|
||||
}
|
||||
}
|
86
pkg/server/secret.go
Normal file
86
pkg/server/secret.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
||||
"github.com/rancher/os2/pkg/clients"
|
||||
roscontrollers "github.com/rancher/os2/pkg/generated/controllers/rancheros.cattle.io/v1"
|
||||
corecontrollers "github.com/rancher/wrangler/pkg/generated/controllers/core/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
|
||||
type sharedSecretAuth struct {
|
||||
secretCache corecontrollers.SecretCache
|
||||
machineCache roscontrollers.MachineInventoryCache
|
||||
}
|
||||
|
||||
func newSharedSecretAuth(clients *clients.Clients) *sharedSecretAuth {
|
||||
server := &sharedSecretAuth{
|
||||
secretCache: clients.Core.Secret().Cache(),
|
||||
machineCache: clients.OS.MachineInventory().Cache(),
|
||||
}
|
||||
|
||||
server.secretCache.AddIndexer(tokenIndex, func(obj *corev1.Secret) ([]string, error) {
|
||||
if string(obj.Type) != tokenType {
|
||||
return nil, nil
|
||||
}
|
||||
t := obj.Data[tokenKey]
|
||||
if len(t) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return []string{base64.StdEncoding.EncodeToString(t)}, nil
|
||||
})
|
||||
|
||||
server.machineCache.AddIndexer(machineBySecretNameIndex, func(obj *v1.MachineInventory) ([]string, error) {
|
||||
if obj.Spec.MachineTokenSecretName == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return []string{obj.Namespace + "/" + obj.Spec.MachineTokenSecretName}, nil
|
||||
})
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
func (s *sharedSecretAuth) Authenticate(resp http.ResponseWriter, req *http.Request, registerNamespace string) (*v1.MachineInventory, bool, io.WriteCloser, error) {
|
||||
token := strings.TrimPrefix(req.Header.Get("Authorization"), "Bearer ")
|
||||
if token == "" || registerNamespace != "" {
|
||||
return nil, true, nil, nil
|
||||
}
|
||||
|
||||
secrets, err := s.secretCache.GetByIndex(tokenIndex, token)
|
||||
if apierrors.IsNotFound(err) || len(secrets) == 0 {
|
||||
return nil, false, nil, fmt.Errorf("token not found")
|
||||
} else if err != nil {
|
||||
return nil, false, nil, err
|
||||
} else if len(secrets) > 1 {
|
||||
logrus.Errorf("Multiple machine secrets with the same value [%s/%s, %s/%s, ...]",
|
||||
secrets[0].Namespace, secrets[0].Name, secrets[1].Namespace, secrets[1].Name)
|
||||
return nil, false, nil, fmt.Errorf("token not found")
|
||||
}
|
||||
|
||||
machines, err := s.machineCache.GetByIndex(machineBySecretNameIndex, secrets[0].Namespace+"/"+secrets[0].Name)
|
||||
if len(machines) > 1 {
|
||||
logrus.Errorf("Multiple machine inventories with the token: %v", machines)
|
||||
}
|
||||
if apierrors.IsNotFound(err) || len(machines) != 1 {
|
||||
return nil, false, nil, fmt.Errorf("machine not found")
|
||||
} else if err != nil {
|
||||
return nil, false, nil, err
|
||||
}
|
||||
return machines[0], false, writeCloser{resp}, nil
|
||||
}
|
||||
|
||||
type writeCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (writeCloser) Close() error {
|
||||
return nil
|
||||
}
|
@@ -5,16 +5,18 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
||||
"github.com/rancher/os2/pkg/clients"
|
||||
ranchercontrollers "github.com/rancher/os2/pkg/generated/controllers/management.cattle.io/v3"
|
||||
roscontrollers "github.com/rancher/os2/pkg/generated/controllers/rancheros.cattle.io/v1"
|
||||
"github.com/rancher/os2/pkg/tpm"
|
||||
v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
|
||||
corecontrollers "github.com/rancher/wrangler/pkg/generated/controllers/core/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
@@ -24,43 +26,40 @@ var (
|
||||
tokenKey = "token"
|
||||
tokenIndex = "tokenIndex"
|
||||
machineBySecretNameIndex = "machineBySecretNameIndex"
|
||||
registrationTokenIndex = "registrationTokenIndex"
|
||||
tpmHashIndex = "tpmHashIndex"
|
||||
)
|
||||
|
||||
type authenticator interface {
|
||||
Authenticate(resp http.ResponseWriter, req *http.Request, registerNamespace string) (*v1.MachineInventory, bool, io.WriteCloser, error)
|
||||
}
|
||||
|
||||
type InventoryServer struct {
|
||||
secretCache corecontrollers.SecretCache
|
||||
settingCache ranchercontrollers.SettingCache
|
||||
secretCache corecontrollers.SecretCache
|
||||
machineCache roscontrollers.MachineInventoryCache
|
||||
machineClient roscontrollers.MachineInventoryClient
|
||||
machineRegistrationCache roscontrollers.MachineRegistrationCache
|
||||
clusterRegistrationToken ranchercontrollers.ClusterRegistrationTokenCache
|
||||
authenticators []authenticator
|
||||
}
|
||||
|
||||
func New(clients *clients.Clients) *InventoryServer {
|
||||
server := &InventoryServer{
|
||||
authenticators: []authenticator{
|
||||
tpm.New(clients),
|
||||
newSharedSecretAuth(clients),
|
||||
},
|
||||
secretCache: clients.Core.Secret().Cache(),
|
||||
settingCache: clients.Rancher.Setting().Cache(),
|
||||
machineCache: clients.OS.MachineInventory().Cache(),
|
||||
machineClient: clients.OS.MachineInventory(),
|
||||
machineRegistrationCache: clients.OS.MachineRegistration().Cache(),
|
||||
settingCache: clients.Rancher.Setting().Cache(),
|
||||
clusterRegistrationToken: clients.Rancher.ClusterRegistrationToken().Cache(),
|
||||
}
|
||||
|
||||
server.secretCache.AddIndexer(tokenIndex, func(obj *corev1.Secret) ([]string, error) {
|
||||
if string(obj.Type) != tokenType {
|
||||
return nil, nil
|
||||
}
|
||||
t := obj.Data[tokenKey]
|
||||
if len(t) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return []string{base64.StdEncoding.EncodeToString(t)}, nil
|
||||
})
|
||||
|
||||
server.machineCache.AddIndexer(machineBySecretNameIndex, func(obj *v1.MachineInventory) ([]string, error) {
|
||||
if obj.Spec.MachineTokenSecretName == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return []string{obj.Namespace + "/" + obj.Spec.MachineTokenSecretName}, nil
|
||||
})
|
||||
|
||||
server.secretCache.AddIndexer(tokenHash, func(obj *corev1.Secret) ([]string, error) {
|
||||
if string(obj.Type) == tokenType {
|
||||
if string(obj.Type) != tokenType {
|
||||
return nil, nil
|
||||
}
|
||||
if token := obj.Data[tokenKey]; len(token) > 0 {
|
||||
@@ -70,54 +69,89 @@ func New(clients *clients.Clients) *InventoryServer {
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
server.machineCache.AddIndexer(tokenHash, func(obj *v1.MachineInventory) ([]string, error) {
|
||||
if obj.Spec.TPMHash == "" {
|
||||
return nil, nil
|
||||
}
|
||||
hash := sha256.Sum256([]byte(obj.Spec.TPMHash))
|
||||
return []string{base64.StdEncoding.EncodeToString(hash[:])}, nil
|
||||
})
|
||||
|
||||
server.machineRegistrationCache.AddIndexer(registrationTokenIndex, func(obj *v1.MachineRegistration) ([]string, error) {
|
||||
if obj.Status.RegistrationToken == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return []string{
|
||||
obj.Status.RegistrationToken,
|
||||
}, nil
|
||||
})
|
||||
|
||||
server.machineCache.AddIndexer(tpmHashIndex, func(obj *v1.MachineInventory) ([]string, error) {
|
||||
if obj.Spec.TPMHash == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return []string{obj.Spec.TPMHash}, nil
|
||||
})
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
func (i *InventoryServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
|
||||
if strings.HasSuffix(req.URL.Path, "/cacerts") {
|
||||
if strings.Contains(req.URL.Path, "/registration/") {
|
||||
i.register(resp, req)
|
||||
} else if strings.HasSuffix(req.URL.Path, "/cacerts") {
|
||||
i.cacerts(resp, req)
|
||||
} else {
|
||||
err := i.handle(resp, req)
|
||||
if err != nil {
|
||||
http.Error(resp, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
i.handle(resp, req)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *InventoryServer) handle(resp http.ResponseWriter, req *http.Request) error {
|
||||
token := strings.TrimPrefix(req.Header.Get("Authorization"), "Bearer ")
|
||||
secrets, err := i.secretCache.GetByIndex(tokenIndex, token)
|
||||
if apierrors.IsNotFound(err) {
|
||||
http.Error(resp, "Token not found", http.StatusNotFound)
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else if len(secrets) > 1 {
|
||||
logrus.Errorf("Multiple machine secrets with the same value [%s/%s, %s/%s, ...]",
|
||||
secrets[0].Namespace, secrets[0].Name, secrets[1].Namespace, secrets[1].Name)
|
||||
http.Error(resp, "Token not found", http.StatusNotFound)
|
||||
return nil
|
||||
func (i *InventoryServer) authMachine(resp http.ResponseWriter, req *http.Request, registerNamespace string) (*v1.MachineInventory, io.WriteCloser, error) {
|
||||
for _, auth := range i.authenticators {
|
||||
machine, cont, writer, err := auth.Authenticate(resp, req, registerNamespace)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if machine != nil || !cont {
|
||||
return machine, writer, nil
|
||||
}
|
||||
}
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
machines, err := i.machineCache.GetByIndex(machineBySecretNameIndex, secrets[0].Namespace+"/"+secrets[0].Name)
|
||||
if len(machines) > 1 {
|
||||
logrus.Errorf("Multiple machine inventories with the token: %v", machines)
|
||||
func writeErr(writer io.Writer, resp http.ResponseWriter, err error) {
|
||||
message := "Unauthorized"
|
||||
if err != nil {
|
||||
message = err.Error()
|
||||
}
|
||||
if apierrors.IsNotFound(err) || len(machines) != 1 {
|
||||
http.Error(resp, "Machine not found", http.StatusNotFound)
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
if writer == nil {
|
||||
http.Error(resp, message, http.StatusUnauthorized)
|
||||
} else {
|
||||
writer.Write([]byte(message))
|
||||
}
|
||||
}
|
||||
|
||||
crt, err := i.clusterRegistrationToken.Get(machines[0].Status.ClusterRegistrationTokenNamespace,
|
||||
machines[0].Status.ClusterRegistrationTokenName)
|
||||
func (i *InventoryServer) handle(resp http.ResponseWriter, req *http.Request) {
|
||||
machine, writer, err := i.authMachine(resp, req, "")
|
||||
if machine == nil || err != nil {
|
||||
writeErr(writer, resp, err)
|
||||
return
|
||||
}
|
||||
defer writer.Close()
|
||||
|
||||
if machine.Spec.ClusterName == "" {
|
||||
writeErr(writer, resp, errors.New("cluster not assigned"))
|
||||
return
|
||||
}
|
||||
crt, err := i.clusterRegistrationToken.Get(machine.Status.ClusterRegistrationTokenNamespace,
|
||||
machine.Status.ClusterRegistrationTokenName)
|
||||
if apierrors.IsNotFound(err) || crt.Status.Token == "" {
|
||||
http.Error(resp, "Cluster token not found", http.StatusNotFound)
|
||||
return nil
|
||||
writeErr(writer, resp, errors.New("cluster token not assigned"))
|
||||
}
|
||||
|
||||
return writeResponse(resp, machines[0], crt)
|
||||
if err := writeResponse(writer, machine, crt); err != nil {
|
||||
writeErr(writer, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
type config struct {
|
||||
@@ -127,11 +161,10 @@ type config struct {
|
||||
InternalAddress string `json:"internalAddress,omitempty"`
|
||||
Taints []string `json:"taints,omitempty"`
|
||||
Labels []string `json:"labels,omitempty"`
|
||||
ConfigValues map[string]string `json:"extraConfig,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
}
|
||||
|
||||
func writeResponse(resp http.ResponseWriter, inventory *v1.MachineInventory, crt *v3.ClusterRegistrationToken) error {
|
||||
func writeResponse(writer io.Writer, inventory *v1.MachineInventory, crt *v3.ClusterRegistrationToken) error {
|
||||
config := config{
|
||||
Role: inventory.Spec.Config.Role,
|
||||
NodeName: inventory.Spec.Config.NodeName,
|
||||
@@ -139,7 +172,6 @@ func writeResponse(resp http.ResponseWriter, inventory *v1.MachineInventory, crt
|
||||
InternalAddress: inventory.Spec.Config.InternalAddress,
|
||||
Taints: nil,
|
||||
Labels: nil,
|
||||
ConfigValues: inventory.Spec.Config.ConfigValues,
|
||||
Token: crt.Status.Token,
|
||||
}
|
||||
for k, v := range inventory.Spec.Config.Labels {
|
||||
@@ -148,6 +180,5 @@ func writeResponse(resp http.ResponseWriter, inventory *v1.MachineInventory, crt
|
||||
for _, taint := range inventory.Spec.Config.Taints {
|
||||
config.Labels = append(config.Labels, taint.ToString())
|
||||
}
|
||||
resp.Header().Set("Content-Type", "application/json")
|
||||
return json.NewEncoder(resp).Encode(config)
|
||||
return json.NewEncoder(writer).Encode(config)
|
||||
}
|
||||
|
214
pkg/tpm/auth_tpm.go
Normal file
214
pkg/tpm/auth_tpm.go
Normal file
@@ -0,0 +1,214 @@
|
||||
package tpm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
"github.com/google/go-attestation/attest"
|
||||
"github.com/gorilla/websocket"
|
||||
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
||||
"github.com/rancher/wrangler/pkg/merr"
|
||||
"github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (a *AuthServer) verifyChain(ek *attest.EK, namespace string) error {
|
||||
secret, err := a.secretCache.Get(namespace, tpmCACert)
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
roots := x509.NewCertPool()
|
||||
_ = roots.AppendCertsFromPEM(secret.Data[corev1.TLSCertKey])
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: roots,
|
||||
}
|
||||
_, err = ek.Certificate.Verify(opts)
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *AuthServer) generateChallenge(ek *attest.EK, attestationData *AttestationData) ([]byte, []byte, error) {
|
||||
ap := attest.ActivationParameters{
|
||||
TPMVersion: attest.TPMVersion20,
|
||||
EK: ek.Public,
|
||||
AK: *attestationData.AK,
|
||||
}
|
||||
|
||||
secret, ec, err := ap.Generate()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("generating challenge: %w", err)
|
||||
}
|
||||
|
||||
challengeBytes, err := json.Marshal(Challenge{EC: ec})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("marshalling challenge: %w", err)
|
||||
}
|
||||
|
||||
return secret, challengeBytes, nil
|
||||
}
|
||||
|
||||
func (a *AuthServer) validateChallenge(secret, resp []byte) error {
|
||||
var response ChallengeResponse
|
||||
if err := json.Unmarshal(resp, &response); err != nil {
|
||||
return fmt.Errorf("unmarshalling challenge response: %w", err)
|
||||
}
|
||||
if !bytes.Equal(secret, response.Secret) {
|
||||
return fmt.Errorf("invalid challenge response")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AuthServer) validHash(ek *attest.EK, registerNamespace string) (*v1.MachineInventory, error) {
|
||||
hashEncoded, err := GetPubHash(ek)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tpm: could not get public key hash: %v", err)
|
||||
}
|
||||
|
||||
if registerNamespace != "" {
|
||||
if err := a.verifyChain(ek, registerNamespace); err != nil {
|
||||
return nil, fmt.Errorf("verifying chain: %w", err)
|
||||
}
|
||||
return &v1.MachineInventory{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: registerNamespace,
|
||||
},
|
||||
Spec: v1.MachineInventorySpec{
|
||||
TPMHash: hashEncoded,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
machines, err := a.machineCache.GetByIndex(machineByHash, hashEncoded)
|
||||
if apierrors.IsNotFound(err) || len(machines) != 1 {
|
||||
if len(machines) > 1 {
|
||||
logrus.Errorf("multiple machines for same hash %s found: %v", hashEncoded, machines)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to find machine")
|
||||
}
|
||||
|
||||
if err := a.verifyChain(ek, machines[0].Namespace); err != nil {
|
||||
return nil, fmt.Errorf("verifying chain: %w", err)
|
||||
}
|
||||
|
||||
return machines[0], nil
|
||||
}
|
||||
|
||||
func writeRead(conn *websocket.Conn, input []byte) ([]byte, error) {
|
||||
writer, err := conn.NextWriter(websocket.BinaryMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := writer.Write(input); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := writer.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, reader, err := conn.NextReader()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ioutil.ReadAll(reader)
|
||||
}
|
||||
|
||||
func upgrade(resp http.ResponseWriter, req *http.Request) (*websocket.Conn, error) {
|
||||
upgrader := websocket.Upgrader{
|
||||
HandshakeTimeout: 5 * time.Second,
|
||||
CheckOrigin: func(r *http.Request) bool { return true },
|
||||
}
|
||||
|
||||
conn, err := upgrader.Upgrade(resp, req, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
||||
_ = conn.SetReadDeadline(time.Now().Add(10 * time.Second))
|
||||
|
||||
return conn, err
|
||||
}
|
||||
|
||||
func (a *AuthServer) getAttestationData(header string) (*attest.EK, *AttestationData, error) {
|
||||
tpmBytes, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(header, "Bearer TPM"))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var attestationData AttestationData
|
||||
if err := json.Unmarshal(tpmBytes, &attestationData); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ek, err := DecodeEK(attestationData.EK)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return ek, &attestationData, nil
|
||||
}
|
||||
|
||||
func (a *AuthServer) Authenticate(resp http.ResponseWriter, req *http.Request, registerNamespace string) (*v1.MachineInventory, bool, io.WriteCloser, error) {
|
||||
header := req.Header.Get("Authorization")
|
||||
if !strings.HasPrefix(header, "Bearer TPM") {
|
||||
return nil, true, nil, nil
|
||||
}
|
||||
|
||||
ek, attestationData, err := a.getAttestationData(header)
|
||||
if err != nil {
|
||||
return nil, false, nil, err
|
||||
}
|
||||
|
||||
machine, err := a.validHash(ek, registerNamespace)
|
||||
if err != nil {
|
||||
return nil, false, nil, err
|
||||
}
|
||||
|
||||
secret, challenge, err := a.generateChallenge(ek, attestationData)
|
||||
if err != nil {
|
||||
return nil, false, nil, err
|
||||
}
|
||||
|
||||
conn, err := upgrade(resp, req)
|
||||
if err != nil {
|
||||
return nil, false, nil, err
|
||||
}
|
||||
|
||||
challResp, err := writeRead(conn, challenge)
|
||||
if err != nil {
|
||||
return nil, false, nil, err
|
||||
}
|
||||
|
||||
if err := a.validateChallenge(secret, challResp); err != nil {
|
||||
return nil, false, nil, err
|
||||
}
|
||||
|
||||
writer, err := conn.NextWriter(websocket.BinaryMessage)
|
||||
return machine, false, &responseWriter{
|
||||
WriteCloser: writer,
|
||||
conn: conn,
|
||||
}, err
|
||||
}
|
||||
|
||||
type responseWriter struct {
|
||||
io.WriteCloser
|
||||
conn *websocket.Conn
|
||||
}
|
||||
|
||||
func (r *responseWriter) Close() error {
|
||||
err := r.WriteCloser.Close()
|
||||
err2 := r.conn.Close()
|
||||
return merr.NewErrors(err, err2)
|
||||
}
|
36
pkg/tpm/tpm.go
Normal file
36
pkg/tpm/tpm.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package tpm
|
||||
|
||||
import (
|
||||
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
||||
"github.com/rancher/os2/pkg/clients"
|
||||
roscontrollers "github.com/rancher/os2/pkg/generated/controllers/rancheros.cattle.io/v1"
|
||||
corecontrollers "github.com/rancher/wrangler/pkg/generated/controllers/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
machineByHash = "machineByHash"
|
||||
tpmCACert = "tpm-ca"
|
||||
)
|
||||
|
||||
type AuthServer struct {
|
||||
machineCache roscontrollers.MachineInventoryCache
|
||||
machineClient roscontrollers.MachineInventoryClient
|
||||
secretCache corecontrollers.SecretCache
|
||||
}
|
||||
|
||||
func New(clients *clients.Clients) *AuthServer {
|
||||
a := &AuthServer{
|
||||
machineCache: clients.OS.MachineInventory().Cache(),
|
||||
machineClient: clients.OS.MachineInventory(),
|
||||
secretCache: clients.Core.Secret().Cache(),
|
||||
}
|
||||
|
||||
a.machineCache.AddIndexer(machineByHash, func(obj *v1.MachineInventory) ([]string, error) {
|
||||
if obj.Spec.TPMHash == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return []string{obj.Spec.TPMHash}, nil
|
||||
})
|
||||
|
||||
return a
|
||||
}
|
94
pkg/tpm/tpm_attestor.go
Normal file
94
pkg/tpm/tpm_attestor.go
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
** Copyright 2019 Bloomberg Finance L.P.
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
package tpm
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
"github.com/google/go-attestation/attest"
|
||||
)
|
||||
|
||||
type AttestationData struct {
|
||||
EK []byte
|
||||
AK *attest.AttestationParameters
|
||||
}
|
||||
|
||||
type Challenge struct {
|
||||
EC *attest.EncryptedCredential
|
||||
}
|
||||
|
||||
type KeyData struct {
|
||||
Keys []string `json:"keys"`
|
||||
}
|
||||
|
||||
type ChallengeResponse struct {
|
||||
Secret []byte
|
||||
}
|
||||
|
||||
func GetPubHash(ek *attest.EK) (string, error) {
|
||||
data, err := pubBytes(ek)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pubHash := sha256.Sum256(data)
|
||||
hashEncoded := fmt.Sprintf("%x", pubHash)
|
||||
return hashEncoded, nil
|
||||
}
|
||||
|
||||
func pubBytes(ek *attest.EK) ([]byte, error) {
|
||||
data, err := x509.MarshalPKIXPublicKey(ek.Public)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshaling ec public key: %v", err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func DecodeEK(pemBytes []byte) (*attest.EK, error) {
|
||||
block, _ := pem.Decode(pemBytes)
|
||||
|
||||
if block == nil {
|
||||
return nil, errors.New("invalid pemBytes")
|
||||
}
|
||||
|
||||
switch block.Type {
|
||||
case "CERTIFICATE":
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing certificate: %v", err)
|
||||
}
|
||||
return &attest.EK{
|
||||
Certificate: cert,
|
||||
Public: cert.PublicKey,
|
||||
}, nil
|
||||
|
||||
case "PUBLIC KEY":
|
||||
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing ecdsa public key: %v", err)
|
||||
}
|
||||
|
||||
return &attest.EK{
|
||||
Public: pub,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid pem type: %s", block.Type)
|
||||
}
|
@@ -27,7 +27,7 @@ RUN cd /usr/sbin && \
|
||||
rm tmp
|
||||
RUN cd /usr/src && \
|
||||
git clone https://github.com/rancher-sandbox/cOS-toolkit
|
||||
RUN curl -Lo /usr/bin/luet https://github.com/mudler/luet/releases/download/0.18.1/luet-0.18.1-linux-$(go env GOARCH) && \
|
||||
RUN curl -Lo /usr/bin/luet https://github.com/mudler/luet/releases/download/0.20.5/luet-0.20.5-linux-$(go env GOARCH) && \
|
||||
chmod +x /usr/bin/luet
|
||||
RUN mkdir -p /iso/iso-overlay/boot/grub2 /etc/luet
|
||||
RUN export SUFFIX; \
|
||||
@@ -104,7 +104,7 @@ RUN echo -e \
|
||||
'fi\n'\
|
||||
'menuentry "RancherOS Install" --class os --unrestricted {\n'\
|
||||
' echo Loading kernel...\n'\
|
||||
' $linux ($root)/boot/kernel.xz cdroot root=live:CDLABEL=COS_LIVE rd.live.dir=/ rd.live.squashimg=rootfs.squashfs console=tty1 console=ttyS0 rd.cos.disable\n'\
|
||||
' $linux ($root)/boot/kernel.xz cdroot root=live:CDLABEL=COS_LIVE rd.live.dir=/ rd.live.squashimg=rootfs.squashfs console=tty1 console=ttyS0 rd.cos.disable rancheros.install.automatic=true rancheros.install.config_url=/run/initramfs/live/config\n'\
|
||||
' echo Loading initrd...\n'\
|
||||
' $initrd ($root)/boot/rootfs.xz\n'\
|
||||
'}\n'\
|
||||
@@ -115,7 +115,13 @@ RUN echo -e \
|
||||
' terminal_output console\n'\
|
||||
' }\n'\
|
||||
'fi\n' > /iso/iso-overlay/boot/grub2/grub.cfg
|
||||
RUN echo -e '#cloud-config\n'\
|
||||
'rancheros:\n'\
|
||||
' install:\n'\
|
||||
' automatic: false\n' > /iso/iso-overlay/config
|
||||
RUN luet install --no-spinner -y toolchain/luet-makeiso
|
||||
ARG CONFIG
|
||||
RUN if [ -n "$CONFIG" ]; then echo "$CONFIG" > /iso/iso-overlay/config; fi
|
||||
WORKDIR /usr/src/cOS-toolkit/packer
|
||||
|
||||
FROM tools AS iso-build
|
||||
@@ -179,7 +185,10 @@ EOF
|
||||
|
||||
iso()
|
||||
{
|
||||
build --target iso -o build/
|
||||
if [ -n "$CONFIG" ]; then
|
||||
CONFIG_DATA="$(<$CONFIG)"
|
||||
fi
|
||||
build --target iso -o build/ --build-arg CONFIG="${CONFIG_DATA}"
|
||||
}
|
||||
|
||||
qcow()
|
||||
@@ -221,14 +230,16 @@ ami()
|
||||
usage()
|
||||
{
|
||||
echo "Usage:"
|
||||
echo " $0 IMAGE OUTPUT"
|
||||
echo " $0 IMAGE OUTPUT [ISO_CLOUD_CONFIG]"
|
||||
echo
|
||||
echo " IMAGE: a Docker image"
|
||||
echo " OUTPUT: Comma seperated value of output image formats. Valid: aws,iso,qcow"
|
||||
echo " ISO_CLOUD_CONFIG: An option file that will be used as the default cloud-init in an ISO"
|
||||
}
|
||||
|
||||
IMAGE=$1
|
||||
OUTPUT=$2
|
||||
CONFIG=$3
|
||||
VERSION=${IMAGE##*:}
|
||||
NAME=${IMAGE%%:${VERSION}}
|
||||
NAME=${NAME//[^a-zA-Z0-9-@.\/_]/-}
|
||||
@@ -258,6 +269,8 @@ fi
|
||||
;;
|
||||
*)
|
||||
echo Unknown format $i
|
||||
echo
|
||||
usage
|
||||
exit 1
|
||||
esac
|
||||
done
|
||||
|
3
scripts/install-chart
Executable file
3
scripts/install-chart
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
helm -n cattle-rancheros-operator-system upgrade --install --create-namespace rancheros-operator ./chart
|
13
scripts/qemu-in-container
Executable file
13
scripts/qemu-in-container
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$#" = 0 ]; then
|
||||
exec bash
|
||||
fi
|
||||
|
||||
if [ -e /dev/tpm0 ]; then
|
||||
mkdir /tmp/emulated_tpm
|
||||
swtpm socket --tpmstate dir=/tmp/emulated_tpm --ctrl type=unixio,path=/tmp/emulated_tpm/swtpm-sock --log level=1 --tpm2 &
|
||||
|
||||
exec "$@" -chardev socket,id=chrtpm,path=/tmp/emulated_tpm/swtpm-sock \
|
||||
-tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0
|
||||
fi
|
6
scripts/qemu-wrapper
Executable file
6
scripts/qemu-wrapper
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -x -e
|
||||
|
||||
BASE=$(dirname $0)/..
|
||||
docker build -t ros-qemu -f ${BASE}/Dockerfile.kvm ${BASE}
|
||||
exec docker run -it --rm --net=host -v $(dirname $(pwd)):$(dirname $(pwd)) -w $(pwd) --privileged ros-qemu "$@"
|
@@ -38,7 +38,8 @@ if [ "$1" == "" ] && [ ! -e output.iso ]; then
|
||||
fi
|
||||
|
||||
#-bios /usr/share/qemu/OVMF.fd \
|
||||
qemu-system-x86_64 \
|
||||
|
||||
../scripts/qemu-wrapper qemu-system-x86_64 \
|
||||
$BOOT \
|
||||
-enable-kvm \
|
||||
-m ${MEMORY:=4096} \
|
||||
|
Reference in New Issue
Block a user