mirror of
https://github.com/kairos-io/osbuilder.git
synced 2025-12-24 20:34:11 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78f2008759 | ||
|
|
85adc00f5a | ||
|
|
8a9b3e9f71 | ||
|
|
78ff22e647 | ||
|
|
4522e14e32 | ||
|
|
097b4a30db | ||
|
|
40560a4b02 | ||
|
|
4997cf18ee | ||
|
|
7ae1f7105a | ||
|
|
ebbd1c9a1a | ||
|
|
82847a139d | ||
|
|
f5611e684f | ||
|
|
6b7ae5af02 | ||
|
|
ece128a0b5 | ||
|
|
4b2be221b3 | ||
|
|
4f87e2329c | ||
|
|
dc6fb2c6be | ||
|
|
dc55928694 | ||
|
|
1f932a7644 | ||
|
|
5c9e0a35e4 | ||
|
|
a2e9c158be | ||
|
|
0bfe296c53 | ||
|
|
669714d915 | ||
|
|
5e5b3af940 | ||
|
|
ca8065f94b | ||
|
|
8706980f2b | ||
|
|
2309c0f175 | ||
|
|
086dbca453 | ||
|
|
658c87a111 | ||
|
|
6cafc07e65 | ||
|
|
f4a4829eb0 | ||
|
|
92749af928 | ||
|
|
44e07e8218 | ||
|
|
4dcfcc4172 | ||
|
|
9ce274af56 |
8
Makefile
8
Makefile
@@ -276,4 +276,10 @@ unit-tests: test_deps
|
||||
e2e-tests:
|
||||
GINKGO=$(GINKGO) KUBE_VERSION=${KUBE_VERSION} $(ROOT_DIR)/script/test.sh
|
||||
|
||||
kind-e2e-tests: ginkgo kind-setup install undeploy-dev deploy-dev e2e-tests
|
||||
kind-e2e-tests: ginkgo kind-setup install undeploy-dev deploy-dev e2e-tests
|
||||
|
||||
|
||||
kubesplit: manifests kustomize
|
||||
rm -rf helm-chart
|
||||
mkdir helm-chart
|
||||
$(KUSTOMIZE) build config/default | kubesplit -helm helm-chart
|
||||
9
PROJECT
9
PROJECT
@@ -16,4 +16,13 @@ resources:
|
||||
kind: OSArtifact
|
||||
path: github.com/kairos-io/osbuilder/api/v1alpha1
|
||||
version: v1alpha1
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: true
|
||||
controller: true
|
||||
domain: kairos.io
|
||||
group: build
|
||||
kind: NetbootDeployment
|
||||
path: github.com/kairos-io/osbuilder/api/v1alpha1
|
||||
version: v1alpha1
|
||||
version: "3"
|
||||
|
||||
110
README.md
110
README.md
@@ -1,94 +1,30 @@
|
||||
# osartifactbuilder-operator
|
||||
// TODO(user): Add simple overview of use/purpose
|
||||
# osbuilder
|
||||
|
||||
## Description
|
||||
// TODO(user): An in-depth paragraph about your project and overview of use
|
||||
| :exclamation: | This is experimental! |
|
||||
|-|:-|
|
||||
|
||||
## Getting Started
|
||||
You’ll need a Kubernetes cluster to run against. You can use [KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or run against a remote cluster.
|
||||
**Note:** Your controller will automatically use the current context in your kubeconfig file (i.e. whatever cluster `kubectl cluster-info` shows).
|
||||
This is the Kairos osbuilder Kubernetes Native Extension.
|
||||
|
||||
### Running on the cluster
|
||||
1. Install Instances of Custom Resources:
|
||||
To install, use helm:
|
||||
|
||||
```sh
|
||||
kubectl apply -f config/samples/
|
||||
```
|
||||
# Adds the kairos repo to helm
|
||||
$ helm repo add kairos https://kairos-io.github.io/helm-charts
|
||||
"kairos" has been added to your repositories
|
||||
$ helm repo update
|
||||
Hang tight while we grab the latest from your chart repositories...
|
||||
...Successfully got an update from the "kairos" chart repository
|
||||
Update Complete. ⎈Happy Helming!⎈
|
||||
|
||||
2. Build and push your image to the location specified by `IMG`:
|
||||
|
||||
```sh
|
||||
make docker-build docker-push IMG=<some-registry>/osartifactbuilder-operator:tag
|
||||
# Install the CRD chart
|
||||
$ helm install kairos-crd kairos/kairos-crds
|
||||
NAME: kairos-crd
|
||||
LAST DEPLOYED: Tue Sep 6 20:35:34 2022
|
||||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
REVISION: 1
|
||||
TEST SUITE: None
|
||||
|
||||
# Installs osbuilder
|
||||
$ helm install kairos-osbuilder kairos/osbuilder
|
||||
```
|
||||
|
||||
3. Deploy the controller to the cluster with the image specified by `IMG`:
|
||||
|
||||
```sh
|
||||
make deploy IMG=<some-registry>/osartifactbuilder-operator:tag
|
||||
```
|
||||
|
||||
### Uninstall CRDs
|
||||
To delete the CRDs from the cluster:
|
||||
|
||||
```sh
|
||||
make uninstall
|
||||
```
|
||||
|
||||
### Undeploy controller
|
||||
UnDeploy the controller to the cluster:
|
||||
|
||||
```sh
|
||||
make undeploy
|
||||
```
|
||||
|
||||
## Contributing
|
||||
// TODO(user): Add detailed information on how you would like others to contribute to this project
|
||||
|
||||
### How it works
|
||||
This project aims to follow the Kubernetes [Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/)
|
||||
|
||||
It uses [Controllers](https://kubernetes.io/docs/concepts/architecture/controller/)
|
||||
which provides a reconcile function responsible for synchronizing resources untile the desired state is reached on the cluster
|
||||
|
||||
### Test It Out
|
||||
1. Install the CRDs into the cluster:
|
||||
|
||||
```sh
|
||||
make install
|
||||
```
|
||||
|
||||
2. Run your controller (this will run in the foreground, so switch to a new terminal if you want to leave it running):
|
||||
|
||||
```sh
|
||||
make run
|
||||
```
|
||||
|
||||
**NOTE:** You can also run this in one step by running: `make install run`
|
||||
|
||||
### Modifying the API definitions
|
||||
If you are editing the API definitions, generate the manifests such as CRs or CRDs using:
|
||||
|
||||
```sh
|
||||
make manifests
|
||||
```
|
||||
|
||||
**NOTE:** Run `make --help` for more information on all potential `make` targets
|
||||
|
||||
More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html)
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2022.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
63
api/v1alpha1/netbootdeployment_types.go
Normal file
63
api/v1alpha1/netbootdeployment_types.go
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright 2022.
|
||||
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
|
||||
// NetbootDeploymentSpec defines the desired state of NetbootDeployment
|
||||
type NetbootDeploymentSpec struct {
|
||||
CloudConfig string `json:"cloudConfig,omitempty"`
|
||||
CommandLine string `json:"cmdLine,omitempty"`
|
||||
|
||||
// TODO: Those below should be deprecated and reference an osbuild instead
|
||||
}
|
||||
|
||||
// NetbootDeploymentStatus defines the observed state of NetbootDeployment
|
||||
type NetbootDeploymentStatus struct {
|
||||
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
//+kubebuilder:subresource:status
|
||||
|
||||
// NetbootDeployment is the Schema for the netbootdeployments API
|
||||
type NetbootDeployment struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec NetbootDeploymentSpec `json:"spec,omitempty"`
|
||||
Status NetbootDeploymentStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
|
||||
// NetbootDeploymentList contains a list of NetbootDeployment
|
||||
type NetbootDeploymentList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []NetbootDeployment `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&NetbootDeployment{}, &NetbootDeploymentList{})
|
||||
}
|
||||
@@ -30,7 +30,18 @@ type OSArtifactSpec struct {
|
||||
|
||||
// Foo is an example field of OSArtifact. Edit osartifact_types.go to remove/update
|
||||
ImageName string `json:"imageName,omitempty"`
|
||||
ISO bool `json:"iso,omitempty"`
|
||||
// This needs to be revisited
|
||||
ISO bool `json:"iso,omitempty"`
|
||||
|
||||
//Disk-only stuff
|
||||
DiskSize string `json:"diskSize,omitempty"`
|
||||
CloudImage bool `json:"cloudImage,omitempty"`
|
||||
AzureImage bool `json:"azureImage,omitempty"`
|
||||
GCEImage bool `json:"gceImage,omitempty"`
|
||||
|
||||
Netboot bool `json:"netboot,omitempty"`
|
||||
NetbootURL string `json:"netbootURL,omitempty"`
|
||||
|
||||
// TODO: treat cloudconfig as a secret, and take a secretRef where to store it (optionally)
|
||||
CloudConfig string `json:"cloudConfig,omitempty"`
|
||||
GRUBConfig string `json:"grubConfig,omitempty"`
|
||||
|
||||
@@ -40,6 +40,95 @@ func (in *LocalObjectReference) DeepCopy() *LocalObjectReference {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NetbootDeployment) DeepCopyInto(out *NetbootDeployment) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
out.Status = in.Status
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetbootDeployment.
|
||||
func (in *NetbootDeployment) DeepCopy() *NetbootDeployment {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NetbootDeployment)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *NetbootDeployment) 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 *NetbootDeploymentList) DeepCopyInto(out *NetbootDeploymentList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]NetbootDeployment, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetbootDeploymentList.
|
||||
func (in *NetbootDeploymentList) DeepCopy() *NetbootDeploymentList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NetbootDeploymentList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *NetbootDeploymentList) 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 *NetbootDeploymentSpec) DeepCopyInto(out *NetbootDeploymentSpec) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetbootDeploymentSpec.
|
||||
func (in *NetbootDeploymentSpec) DeepCopy() *NetbootDeploymentSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NetbootDeploymentSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NetbootDeploymentStatus) DeepCopyInto(out *NetbootDeploymentStatus) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetbootDeploymentStatus.
|
||||
func (in *NetbootDeploymentStatus) DeepCopy() *NetbootDeploymentStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NetbootDeploymentStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *OSArtifact) DeepCopyInto(out *OSArtifact) {
|
||||
*out = *in
|
||||
|
||||
@@ -35,6 +35,8 @@ spec:
|
||||
spec:
|
||||
description: OSArtifactSpec defines the desired state of OSArtifact
|
||||
properties:
|
||||
azureImage:
|
||||
type: boolean
|
||||
bundles:
|
||||
items:
|
||||
type: string
|
||||
@@ -43,6 +45,13 @@ spec:
|
||||
description: 'TODO: treat cloudconfig as a secret, and take a secretRef
|
||||
where to store it (optionally)'
|
||||
type: string
|
||||
cloudImage:
|
||||
type: boolean
|
||||
diskSize:
|
||||
description: Disk-only stuff
|
||||
type: string
|
||||
gceImage:
|
||||
type: boolean
|
||||
grubConfig:
|
||||
type: string
|
||||
imageName:
|
||||
@@ -50,7 +59,12 @@ spec:
|
||||
to remove/update
|
||||
type: string
|
||||
iso:
|
||||
description: This needs to be revisited
|
||||
type: boolean
|
||||
netboot:
|
||||
type: boolean
|
||||
netbootURL:
|
||||
type: string
|
||||
osRelease:
|
||||
type: string
|
||||
pull:
|
||||
@@ -3,17 +3,20 @@
|
||||
# It should be run by config/default
|
||||
resources:
|
||||
- bases/build.kairos.io_osartifacts.yaml
|
||||
- bases/build.kairos.io_netbootdeployments.yaml
|
||||
#+kubebuilder:scaffold:crdkustomizeresource
|
||||
|
||||
patchesStrategicMerge:
|
||||
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
|
||||
# patches here are for enabling the conversion webhook for each CRD
|
||||
#- patches/webhook_in_osartifacts.yaml
|
||||
#- patches/webhook_in_netbootdeployments.yaml
|
||||
#+kubebuilder:scaffold:crdkustomizewebhookpatch
|
||||
|
||||
# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.
|
||||
# patches here are for enabling the CA injection for each CRD
|
||||
#- patches/cainjection_in_osartifacts.yaml
|
||||
#- patches/cainjection_in_netbootdeployments.yaml
|
||||
#+kubebuilder:scaffold:crdkustomizecainjectionpatch
|
||||
|
||||
# the following config is for teaching kustomize how to do kustomization for CRDs.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# The following patch adds a directive for certmanager to inject CA into the CRD
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
|
||||
name: netbootdeployments.build.kairos.io
|
||||
16
config/crd/patches/webhook_in_netbootdeployments.yaml
Normal file
16
config/crd/patches/webhook_in_netbootdeployments.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
# The following patch enables a conversion webhook for the CRD
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: netbootdeployments.build.kairos.io
|
||||
spec:
|
||||
conversion:
|
||||
strategy: Webhook
|
||||
webhook:
|
||||
clientConfig:
|
||||
service:
|
||||
namespace: system
|
||||
name: webhook-service
|
||||
path: /convert
|
||||
conversionReviewVersions:
|
||||
- v1
|
||||
24
config/rbac/netbootdeployment_editor_role.yaml
Normal file
24
config/rbac/netbootdeployment_editor_role.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
# permissions for end users to edit netbootdeployments.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: netbootdeployment-editor-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- build.kairos.io
|
||||
resources:
|
||||
- netbootdeployments
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- build.kairos.io
|
||||
resources:
|
||||
- netbootdeployments/status
|
||||
verbs:
|
||||
- get
|
||||
20
config/rbac/netbootdeployment_viewer_role.yaml
Normal file
20
config/rbac/netbootdeployment_viewer_role.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
# permissions for end users to view netbootdeployments.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: netbootdeployment-viewer-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- build.kairos.io
|
||||
resources:
|
||||
- netbootdeployments
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- build.kairos.io
|
||||
resources:
|
||||
- netbootdeployments/status
|
||||
verbs:
|
||||
- get
|
||||
6
config/samples/build_v1alpha1_netbootdeployment.yaml
Normal file
6
config/samples/build_v1alpha1_netbootdeployment.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: build.kairos.io/v1alpha1
|
||||
kind: NetbootDeployment
|
||||
metadata:
|
||||
name: netbootdeployment-sample
|
||||
spec:
|
||||
# TODO(user): Add fields here
|
||||
@@ -1,4 +1,5 @@
|
||||
## Append samples you want in your CSV to this file as resources ##
|
||||
resources:
|
||||
- build_v1alpha1_osartifact.yaml
|
||||
- build_v1alpha1_netbootdeployment.yaml
|
||||
#+kubebuilder:scaffold:manifestskustomizesamples
|
||||
|
||||
@@ -104,7 +104,8 @@ func osReleaseContainer(containerImage string) v1.Container {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *OSArtifactReconciler) genDeployment(artifact buildv1alpha1.OSArtifact) *appsv1.Deployment {
|
||||
func (r *OSArtifactReconciler) genDeployment(artifact buildv1alpha1.OSArtifact, svc *v1.Service) *appsv1.Deployment {
|
||||
// TODO: svc is unused, but could be used in the future to generate the Netboot URL
|
||||
objMeta := metav1.ObjectMeta{
|
||||
Name: artifact.Name,
|
||||
Namespace: artifact.Namespace,
|
||||
@@ -116,38 +117,134 @@ func (r *OSArtifactReconciler) genDeployment(artifact buildv1alpha1.OSArtifact)
|
||||
privileged := false
|
||||
serviceAccount := false
|
||||
|
||||
cmd := fmt.Sprintf(
|
||||
"/entrypoint.sh --debug --name %s build-iso --date=false --output /public dir:/rootfs",
|
||||
artifact.Name,
|
||||
)
|
||||
|
||||
volumeMounts := []v1.VolumeMount{
|
||||
{
|
||||
Name: "public",
|
||||
MountPath: "/public",
|
||||
},
|
||||
{
|
||||
Name: "rootfs",
|
||||
MountPath: "/rootfs",
|
||||
},
|
||||
}
|
||||
|
||||
if artifact.Spec.GRUBConfig != "" {
|
||||
volumeMounts = append(volumeMounts, v1.VolumeMount{
|
||||
Name: "config",
|
||||
MountPath: "/iso/iso-overlay/boot/grub2/grub.cfg",
|
||||
SubPath: "grub.cfg",
|
||||
})
|
||||
}
|
||||
|
||||
cloudImgCmd := fmt.Sprintf(
|
||||
"/raw-images.sh /rootfs /public/%s.raw",
|
||||
artifact.Name,
|
||||
)
|
||||
|
||||
if artifact.Spec.CloudConfig != "" {
|
||||
volumeMounts = append(volumeMounts, v1.VolumeMount{
|
||||
Name: "config",
|
||||
MountPath: "/iso/iso-overlay/cloud_config.yaml",
|
||||
SubPath: "config",
|
||||
})
|
||||
|
||||
cloudImgCmd += " /iso/iso-overlay/cloud_config.yaml"
|
||||
}
|
||||
|
||||
if artifact.Spec.CloudConfig != "" || artifact.Spec.GRUBConfig != "" {
|
||||
cmd = fmt.Sprintf(
|
||||
"/entrypoint.sh --debug --name %s build-iso --date=false --overlay-iso /iso/iso-overlay --output /public dir:/rootfs",
|
||||
artifact.Name,
|
||||
)
|
||||
}
|
||||
|
||||
buildIsoContainer := v1.Container{
|
||||
ImagePullPolicy: v1.PullAlways,
|
||||
SecurityContext: &v1.SecurityContext{Privileged: &privileged},
|
||||
Name: "build-iso",
|
||||
Image: r.ToolImage,
|
||||
Command: []string{"/bin/bash", "-cxe"},
|
||||
Args: []string{
|
||||
cmd,
|
||||
},
|
||||
VolumeMounts: volumeMounts,
|
||||
}
|
||||
|
||||
buildCloudImageContainer := v1.Container{
|
||||
ImagePullPolicy: v1.PullAlways,
|
||||
SecurityContext: &v1.SecurityContext{Privileged: &privileged},
|
||||
Name: "build-cloud-image",
|
||||
Image: r.ToolImage,
|
||||
|
||||
Command: []string{"/bin/bash", "-cxe"},
|
||||
Args: []string{
|
||||
cloudImgCmd,
|
||||
},
|
||||
VolumeMounts: volumeMounts,
|
||||
}
|
||||
|
||||
if artifact.Spec.DiskSize != "" {
|
||||
buildCloudImageContainer.Env = []v1.EnvVar{{
|
||||
Name: "EXTEND",
|
||||
Value: artifact.Spec.DiskSize,
|
||||
}}
|
||||
}
|
||||
|
||||
extractNetboot := v1.Container{
|
||||
ImagePullPolicy: v1.PullAlways,
|
||||
SecurityContext: &v1.SecurityContext{Privileged: &privileged},
|
||||
Name: "build-netboot",
|
||||
Image: r.ToolImage,
|
||||
Command: []string{"/bin/bash", "-cxe"},
|
||||
Env: []v1.EnvVar{{
|
||||
Name: "URL",
|
||||
Value: artifact.Spec.NetbootURL,
|
||||
}},
|
||||
Args: []string{
|
||||
fmt.Sprintf(
|
||||
"/entrypoint.sh --debug --name %s build-iso --date=false --overlay-iso /iso/iso-overlay --output /public dir:/rootfs",
|
||||
"/netboot.sh /public/%s.iso /public/%s",
|
||||
artifact.Name,
|
||||
artifact.Name,
|
||||
),
|
||||
},
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{
|
||||
Name: "public",
|
||||
MountPath: "/public",
|
||||
},
|
||||
{
|
||||
Name: "config",
|
||||
MountPath: "/iso/iso-overlay/cloud_config.yaml",
|
||||
SubPath: "config",
|
||||
},
|
||||
{
|
||||
Name: "config",
|
||||
MountPath: "/iso/iso-overlay/boot/grub2/grub.cfg",
|
||||
SubPath: "grub.cfg",
|
||||
},
|
||||
{
|
||||
Name: "rootfs",
|
||||
MountPath: "/rootfs",
|
||||
},
|
||||
VolumeMounts: volumeMounts,
|
||||
}
|
||||
|
||||
buildAzureCloudImageContainer := v1.Container{
|
||||
ImagePullPolicy: v1.PullAlways,
|
||||
SecurityContext: &v1.SecurityContext{Privileged: &privileged},
|
||||
Name: "build-azure-cloud-image",
|
||||
Image: r.ToolImage,
|
||||
Command: []string{"/bin/bash", "-cxe"},
|
||||
Args: []string{
|
||||
fmt.Sprintf(
|
||||
"/azure.sh /public/%s.raw /public/%s.vhd",
|
||||
artifact.Name,
|
||||
artifact.Name,
|
||||
),
|
||||
},
|
||||
VolumeMounts: volumeMounts,
|
||||
}
|
||||
|
||||
buildGCECloudImageContainer := v1.Container{
|
||||
ImagePullPolicy: v1.PullAlways,
|
||||
SecurityContext: &v1.SecurityContext{Privileged: &privileged},
|
||||
Name: "build-gce-cloud-image",
|
||||
Image: r.ToolImage,
|
||||
Command: []string{"/bin/bash", "-cxe"},
|
||||
Args: []string{
|
||||
fmt.Sprintf(
|
||||
"/gce.sh /public/%s.raw /public/%s.gce.raw",
|
||||
artifact.Name,
|
||||
artifact.Name,
|
||||
),
|
||||
},
|
||||
VolumeMounts: volumeMounts,
|
||||
}
|
||||
|
||||
servingContainer := v1.Container{
|
||||
@@ -195,11 +292,28 @@ func (r *OSArtifactReconciler) genDeployment(artifact buildv1alpha1.OSArtifact)
|
||||
|
||||
}
|
||||
|
||||
pod.InitContainers = append(pod.InitContainers, buildIsoContainer)
|
||||
if artifact.Spec.ISO || artifact.Spec.Netboot {
|
||||
pod.InitContainers = append(pod.InitContainers, buildIsoContainer)
|
||||
}
|
||||
|
||||
if artifact.Spec.Netboot {
|
||||
pod.InitContainers = append(pod.InitContainers, extractNetboot)
|
||||
}
|
||||
|
||||
if artifact.Spec.CloudImage || artifact.Spec.AzureImage || artifact.Spec.GCEImage {
|
||||
pod.InitContainers = append(pod.InitContainers, buildCloudImageContainer)
|
||||
}
|
||||
|
||||
if artifact.Spec.AzureImage {
|
||||
pod.InitContainers = append(pod.InitContainers, buildAzureCloudImageContainer)
|
||||
}
|
||||
|
||||
if artifact.Spec.GCEImage {
|
||||
pod.InitContainers = append(pod.InitContainers, buildGCECloudImageContainer)
|
||||
}
|
||||
|
||||
if pushImage {
|
||||
pod.InitContainers = append(pod.InitContainers, createImageContainer(r.ToolImage, artifact.Spec.PushOptions))
|
||||
|
||||
}
|
||||
|
||||
pod.Containers = []v1.Container{servingContainer}
|
||||
|
||||
99
controllers/netboot-deployment.go
Normal file
99
controllers/netboot-deployment.go
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2022.
|
||||
|
||||
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 controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
buildv1alpha1 "github.com/kairos-io/osbuilder/api/v1alpha1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (r *NetbootDeploymentReconciler) genDeployment(artifact buildv1alpha1.NetbootDeployment) *appsv1.Deployment {
|
||||
// TODO: svc is unused, but could be used in the future to generate the Netboot URL
|
||||
objMeta := metav1.ObjectMeta{
|
||||
Name: artifact.Name,
|
||||
Namespace: artifact.Namespace,
|
||||
OwnerReferences: genNetbootOwner(artifact),
|
||||
}
|
||||
|
||||
privileged := false
|
||||
serviceAccount := false
|
||||
|
||||
servingContainer := v1.Container{
|
||||
ImagePullPolicy: v1.PullAlways,
|
||||
SecurityContext: &v1.SecurityContext{Privileged: &privileged},
|
||||
Name: "serve",
|
||||
Args: []string{
|
||||
|
||||
"boot",
|
||||
fmt.Sprintf(),
|
||||
fmt.Sprintf(),
|
||||
fmt.Sprintf(),
|
||||
},
|
||||
Image: r.Image,
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{
|
||||
Name: "config",
|
||||
MountPath: "/files/config.yaml",
|
||||
SubPath: "cloudconfig",
|
||||
},
|
||||
},
|
||||
}
|
||||
// boot /files/kairos-core-opensuse-kernel /files/kairos-core-opensuse-initrd --cmdline='rd.neednet=1 ip=dhcp rd.cos.disable root=live:https://github.com/kairos-io/provider-kairos/releases/download/v1.2.0/kairos-alpine-ubuntu-v1.2.0-k3sv1.20.15+k3s1.squashfs netboot nodepair.enable config_url={{ ID "/files/config.yaml" }} console=tty1 console=ttyS0 console=tty0'
|
||||
pod := v1.PodSpec{
|
||||
HostNetwork: true,
|
||||
AutomountServiceAccountToken: &serviceAccount,
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "config",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
ConfigMap: &v1.ConfigMapVolumeSource{
|
||||
LocalObjectReference: v1.LocalObjectReference{Name: fmt.Sprintf("%s-netboot", artifact.Name)}}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pod.Containers = []v1.Container{servingContainer}
|
||||
|
||||
deploymentLabels := genNetDeploymentLabel(artifact.Name)
|
||||
replicas := int32(1)
|
||||
|
||||
return &appsv1.Deployment{
|
||||
ObjectMeta: objMeta,
|
||||
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{MatchLabels: deploymentLabels},
|
||||
Replicas: &replicas,
|
||||
Template: v1.PodTemplateSpec{
|
||||
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: deploymentLabels,
|
||||
},
|
||||
Spec: pod,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
func genNetDeploymentLabel(s string) map[string]string {
|
||||
return map[string]string{
|
||||
"netboot": "serve" + s,
|
||||
}
|
||||
}
|
||||
145
controllers/netbootdeployment_controller.go
Normal file
145
controllers/netbootdeployment_controller.go
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
Copyright 2022.
|
||||
|
||||
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 controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
buildv1alpha1 "github.com/kairos-io/osbuilder/api/v1alpha1"
|
||||
)
|
||||
|
||||
// NetbootDeploymentReconciler reconciles a NetbootDeployment object
|
||||
type NetbootDeploymentReconciler struct {
|
||||
client.Client
|
||||
clientSet *kubernetes.Clientset
|
||||
|
||||
Image string
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
//+kubebuilder:rbac:groups=build.kairos.io,resources=netbootdeployments,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups=build.kairos.io,resources=netbootdeployments/status,verbs=get;update;patch
|
||||
//+kubebuilder:rbac:groups=build.kairos.io,resources=netbootdeployments/finalizers,verbs=update
|
||||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
// TODO(user): Modify the Reconcile function to compare the state specified by
|
||||
// the NetbootDeployment object against the actual cluster state, and then
|
||||
// perform operations to make the cluster state reflect the state specified by
|
||||
// the user.
|
||||
//
|
||||
// For more details, check Reconcile and its Result here:
|
||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile
|
||||
func (r *NetbootDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
logger := log.FromContext(ctx)
|
||||
var netboot buildv1alpha1.NetbootDeployment
|
||||
if err := r.Get(ctx, req.NamespacedName, &netboot); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("Reconciling %v", netboot))
|
||||
// generate configmap required for building a custom image
|
||||
desiredConfigMap := r.genConfigMap(netboot)
|
||||
logger.Info(fmt.Sprintf("Checking configmap %v", netboot))
|
||||
|
||||
cfgMap, err := r.clientSet.CoreV1().ConfigMaps(req.Namespace).Get(ctx, desiredConfigMap.Name, metav1.GetOptions{})
|
||||
if cfgMap == nil || apierrors.IsNotFound(err) {
|
||||
logger.Info(fmt.Sprintf("Creating service %v", desiredConfigMap))
|
||||
|
||||
cfgMap, err = r.clientSet.CoreV1().ConfigMaps(req.Namespace).Create(ctx, desiredConfigMap, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
logger.Error(err, "Failed while creating cfgmap")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
if err != nil {
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
|
||||
desiredDeployment := r.genDeployment(netboot)
|
||||
deployment, err := r.clientSet.AppsV1().Deployments(req.Namespace).Get(ctx, desiredDeployment.Name, metav1.GetOptions{})
|
||||
if deployment == nil || apierrors.IsNotFound(err) {
|
||||
logger.Info(fmt.Sprintf("Creating Deployment %v", deployment))
|
||||
|
||||
deployment, err = r.clientSet.AppsV1().Deployments(req.Namespace).Create(ctx, desiredDeployment, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
logger.Error(err, "Failed while creating deployment")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
return ctrl.Result{Requeue: true}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
|
||||
// TODO(user): your logic here
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
func (r *NetbootDeploymentReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
|
||||
clientset, err := kubernetes.NewForConfig(mgr.GetConfig())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.clientSet = clientset
|
||||
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&buildv1alpha1.NetbootDeployment{}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func genNetbootOwner(artifact buildv1alpha1.NetbootDeployment) []metav1.OwnerReference {
|
||||
return []metav1.OwnerReference{
|
||||
*metav1.NewControllerRef(&artifact.ObjectMeta, schema.GroupVersionKind{
|
||||
Group: buildv1alpha1.GroupVersion.Group,
|
||||
Version: buildv1alpha1.GroupVersion.Version,
|
||||
Kind: "NetbootDeployment",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *NetbootDeploymentReconciler) genConfigMap(artifact buildv1alpha1.NetbootDeployment) *v1.ConfigMap {
|
||||
return &v1.ConfigMap{
|
||||
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%s-netboot", artifact.Name),
|
||||
Namespace: artifact.Namespace,
|
||||
OwnerReferences: genNetbootOwner(artifact),
|
||||
},
|
||||
Data: map[string]string{
|
||||
"cloudconfig": artifact.Spec.CloudConfig,
|
||||
}}
|
||||
}
|
||||
@@ -118,7 +118,7 @@ func (r *OSArtifactReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
||||
}
|
||||
logger.Info(fmt.Sprintf("Checking deployment %v", osbuild))
|
||||
|
||||
desiredDeployment := r.genDeployment(osbuild)
|
||||
desiredDeployment := r.genDeployment(osbuild, svc)
|
||||
deployment, err := r.clientSet.AppsV1().Deployments(req.Namespace).Get(ctx, desiredDeployment.Name, v1.GetOptions{})
|
||||
if deployment == nil || apierrors.IsNotFound(err) {
|
||||
logger.Info(fmt.Sprintf("Creating Deployment %v", deployment))
|
||||
|
||||
7
main.go
7
main.go
@@ -103,6 +103,13 @@ func main() {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "OSArtifact")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err = (&controllers.NetbootDeploymentReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "NetbootDeployment")
|
||||
os.Exit(1)
|
||||
}
|
||||
//+kubebuilder:scaffold:builder
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
|
||||
@@ -1,23 +1,94 @@
|
||||
ARG ELEMENTAL_CLI_VERSION=v0.0.15-ae4f000
|
||||
ARG ELEMENTAL_CLI_VERSION=0.20221107.1
|
||||
ARG LEAP_VERSION=15.4
|
||||
ARG LUET_VERSION=0.32.5
|
||||
FROM quay.io/costoolkit/elemental-cli:$ELEMENTAL_CLI_VERSION AS elemental
|
||||
ARG LUET_VERSION=0.33.0
|
||||
FROM quay.io/kairos/packages:elemental-cli-system-$ELEMENTAL_CLI_VERSION AS elemental
|
||||
FROM quay.io/luet/base:$LUET_VERSION AS luet
|
||||
FROM quay.io/costoolkit/releases-teal:grub2-live-0.0.3-2 AS grub2
|
||||
FROM quay.io/costoolkit/releases-teal:grub2-efi-image-live-0.0.2-2 AS efi
|
||||
|
||||
### TODO: Replace those naked Dockerfiles copies with luet install so we can keep track of all versioning with 1 repository tag
|
||||
### 1) Add the kairos repository with a reference
|
||||
### 2) populate folders accordingly
|
||||
|
||||
## amd64 Live CD artifacts
|
||||
FROM quay.io/kairos/packages:grub2-livecd-0.0.4 AS grub2
|
||||
FROM quay.io/kairos/packages:grub2-efi-image-livecd-0.0.4 AS efi
|
||||
|
||||
## RPI64
|
||||
|
||||
## Firmware is in the amd64 repo (noarch)
|
||||
FROM quay.io/kairos/packages:u-boot-rpi64-firmware-2021.01-5.1 AS rpi-u-boot
|
||||
FROM quay.io/kairos/packages:raspberrypi-firmware-firmware-2021.03.10-2.1 AS rpi-firmware
|
||||
FROM quay.io/kairos/packages:raspberrypi-firmware-config-firmware-2021.03.10-2.1 AS rpi-firmware-config
|
||||
FROM quay.io/kairos/packages:raspberrypi-firmware-dt-firmware-2021.03.15-2.1 AS rpi-firmware-dt
|
||||
|
||||
## PineBook64 Pro
|
||||
FROM quay.io/kairos/packages:u-boot-rockchip-arm-vendor-blob-0.1 AS pinebook-u-boot
|
||||
|
||||
## Generic ARM artifacts
|
||||
FROM quay.io/kairos/packages-arm64:grub-efi-static-0.1 AS grub-efi
|
||||
FROM quay.io/kairos/packages-arm64:grub-config-static-0.1 AS grub-config
|
||||
FROM quay.io/kairos/packages-arm64:grub-artifacts-static-0.1 AS grub-artifacts
|
||||
|
||||
## RAW images
|
||||
FROM quay.io/kairos/packages:grub-efi-static-0.1 AS grub-raw-efi
|
||||
FROM quay.io/kairos/packages:grub-config-static-0.1 AS grub-raw-config
|
||||
FROM quay.io/kairos/packages:grub-artifacts-static-0.1 AS grub-raw-artifacts
|
||||
|
||||
FROM opensuse/leap:$LEAP_VERSION
|
||||
COPY --from=elemental /usr/bin/elemental /usr/bin/elemental
|
||||
COPY --from=luet /usr/bin/luet /usr/bin/luet
|
||||
|
||||
# x86_64 ISOs
|
||||
COPY --from=grub2 / /grub2
|
||||
COPY --from=efi / /efi
|
||||
|
||||
# RAW images
|
||||
COPY --from=grub-raw-efi / /raw/grub
|
||||
COPY --from=grub-raw-config / /raw/grubconfig
|
||||
COPY --from=grub-raw-artifacts / /raw/grubartifacts
|
||||
|
||||
# RPI64
|
||||
COPY --from=rpi-u-boot / /rpi/u-boot
|
||||
COPY --from=rpi-firmware / /rpi/rpi-firmware
|
||||
COPY --from=rpi-firmware-config / /rpi/rpi-firmware-config
|
||||
COPY --from=rpi-firmware-dt / /rpi/rpi-firmware-dt
|
||||
|
||||
# Pinebook
|
||||
COPY --from=pinebook-u-boot / /pinebookpro/u-boot
|
||||
|
||||
# Generic
|
||||
COPY --from=grub-efi / /arm/grub/efi
|
||||
COPY --from=grub-config / /arm/grub/config
|
||||
COPY --from=grub-artifacts / /arm/grub/artifacts
|
||||
|
||||
RUN zypper ref && zypper dup -y
|
||||
RUN zypper ref && zypper in -y xfsprogs parted util-linux-systemd e2fsprogs util-linux udev rsync grub2 dosfstools grub2-x86_64-efi squashfs mtools xorriso lvm2
|
||||
|
||||
## ISO Build depedencies
|
||||
RUN zypper ref && zypper in -y xfsprogs parted util-linux-systemd e2fsprogs curl util-linux udev rsync grub2 dosfstools grub2-x86_64-efi squashfs mtools xorriso lvm2
|
||||
RUN mkdir /config
|
||||
|
||||
# Arm image build deps
|
||||
RUN zypper in -y jq docker git curl gptfdisk kpartx sudo
|
||||
# Netboot
|
||||
RUN zypper in -y cdrtools
|
||||
# cloud images
|
||||
RUN zypper in -y bc qemu-tools
|
||||
|
||||
# ISO build config
|
||||
COPY ./config.yaml /config/manifest.yaml
|
||||
COPY ./entrypoint.sh /entrypoint.sh
|
||||
COPY ./add-cloud-init.sh /add-cloud-init.sh
|
||||
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
# ARM helpers
|
||||
COPY ./build-arm-image.sh /build-arm-image.sh
|
||||
COPY ./arm /arm
|
||||
|
||||
# RAW images helpers
|
||||
COPY ./gce.sh /gce.sh
|
||||
COPY ./raw-images.sh /raw-images.sh
|
||||
COPY ./azure.sh /azure.sh
|
||||
COPY ./netboot.sh /netboot.sh
|
||||
|
||||
COPY defaults.yaml /defaults.yaml
|
||||
|
||||
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
|
||||
18
tools-image/add-cloud-init.sh
Executable file
18
tools-image/add-cloud-init.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
# docker run --entrypoint /add-cloud-init.sh -v $PWD:/work -ti --rm test https://github.com/kairos-io/kairos/releases/download/v1.1.2/kairos-alpine-v1.1.2.iso /work/test.iso /work/config.yaml
|
||||
|
||||
set -ex
|
||||
|
||||
ISO=$1
|
||||
OUT=$2
|
||||
CONFIG=$3
|
||||
|
||||
case ${ISO} in
|
||||
http*)
|
||||
curl -L "${ISO}" -o in.iso
|
||||
ISO=in.iso
|
||||
;;
|
||||
esac
|
||||
|
||||
# Needs xorriso >=1.5.4
|
||||
xorriso -indev $ISO -outdev $OUT -map $CONFIG /config.yaml -boot_image any replay
|
||||
21
tools-image/arm/boards/odroid_c2.sh
Executable file
21
tools-image/arm/boards/odroid_c2.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
image=$1
|
||||
|
||||
if [ -z "$image" ]; then
|
||||
echo "No image specified"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -e "$WORKDIR/luet.yaml" ]; then
|
||||
ls -liah $WORKDIR
|
||||
echo "No valid config file"
|
||||
cat "$WORKDIR/luet.yaml"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sudo luet install --config $WORKDIR/luet.yaml -y --system-target $WORKDIR firmware/odroid-c2
|
||||
# conv=notrunc ?
|
||||
dd if=$WORKDIR/bl1.bin.hardkernel of=$image conv=fsync bs=1 count=442
|
||||
dd if=$WORKDIR/bl1.bin.hardkernel of=$image conv=fsync bs=512 skip=1 seek=1
|
||||
dd if=$WORKDIR/u-boot.odroidc2 of=$image conv=fsync bs=512 seek=97
|
||||
19
tools-image/arm/boards/pinebookpro.sh
Executable file
19
tools-image/arm/boards/pinebookpro.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
image=$1
|
||||
|
||||
if [ -z "$image" ]; then
|
||||
echo "No image specified"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LOADER_OFFSET=${LOADER_OFFSET:-"64"}
|
||||
LOADER_IMAGE=${LOADER_IMAGE:-"idbloader.img"}
|
||||
UBOOT_IMAGE=${UBOOT_IMAGE:-"u-boot.itb"}
|
||||
UBOOT_OFFSET=${UBOOT_OFFSET:-"16384"}
|
||||
|
||||
echo "Writing idbloader"
|
||||
dd conv=notrunc if=/pinebookpro/u-boot/usr/lib/u-boot/pinebook-pro-rk3399/${LOADER_IMAGE} of="$image" conv=fsync seek=${LOADER_OFFSET}
|
||||
echo "Writing u-boot image"
|
||||
dd conv=notrunc if=/pinebookpro/u-boot/usr/lib/u-boot/pinebook-pro-rk3399/${UBOOT_IMAGE} of="$image" conv=fsync seek=${UBOOT_OFFSET}
|
||||
sync $image
|
||||
24
tools-image/arm/boards/rpi64.sh
Executable file
24
tools-image/arm/boards/rpi64.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
partprobe
|
||||
|
||||
kpartx -va $DRIVE
|
||||
|
||||
image=$1
|
||||
|
||||
if [ -z "$image" ]; then
|
||||
echo "No image specified"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -ax
|
||||
TEMPDIR="$(mktemp -d)"
|
||||
echo $TEMPDIR
|
||||
mount "${device}p1" "${TEMPDIR}"
|
||||
|
||||
for dir in /rpi/u-boot /rpi/rpi-firmware /rpi/rpi-firmware-config /rpi/rpi-firmware-dt
|
||||
do
|
||||
cp -rfv ${dir}/* $TEMPDIR
|
||||
done
|
||||
|
||||
umount "${TEMPDIR}"
|
||||
17
tools-image/azure.sh
Executable file
17
tools-image/azure.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Transform a raw image disk to azure vhd
|
||||
RAWIMAGE="$1"
|
||||
VHDDISK="${2:-disk.vhd}"
|
||||
cp -rf $RAWIMAGE $VHDDISK.work
|
||||
|
||||
MB=$((1024*1024))
|
||||
size=$(qemu-img info -f raw --output json "$RAWIMAGE" | gawk 'match($0, /"virtual-size": ([0-9]+),/, val) {print val[1]}')
|
||||
# shellcheck disable=SC2004
|
||||
ROUNDED_SIZE=$(((($size+$MB-1)/$MB)*$MB))
|
||||
echo "Resizing raw image to $ROUNDED_SIZE"
|
||||
qemu-img resize -f raw "$VHDDISK.work" $ROUNDED_SIZE
|
||||
echo "Converting $RAWIMAGE to $VHDDISK"
|
||||
qemu-img convert -f raw -o subformat=fixed,force_size -O vpc "$VHDDISK.work" "$VHDDISK"
|
||||
echo "Done"
|
||||
rm -rf "$VHDDISK.work"
|
||||
426
tools-image/build-arm-image.sh
Executable file
426
tools-image/build-arm-image.sh
Executable file
@@ -0,0 +1,426 @@
|
||||
#!/bin/bash
|
||||
|
||||
## This is a re-adaptation of https://github.com/rancher/elemental-toolkit/blob/main/images/arm-img-builder.sh
|
||||
|
||||
set -ex
|
||||
|
||||
load_vars() {
|
||||
|
||||
model=${MODEL:-odroid_c2}
|
||||
|
||||
directory=${DIRECTORY:-}
|
||||
output_image="${OUTPUT_IMAGE:-arm.img}"
|
||||
# Img creation options. Size is in MB for all of the vars below
|
||||
size="${SIZE:-7544}"
|
||||
state_size="${STATE_SIZE:-4992}"
|
||||
recovery_size="${RECOVERY_SIZE:-2192}"
|
||||
default_active_size="${DEFAULT_ACTIVE_SIZE:-2400}"
|
||||
|
||||
## Repositories
|
||||
final_repo="${FINAL_REPO:-quay.io/costoolkit/releases-teal-arm64}"
|
||||
repo_type="${REPO_TYPE:-docker}"
|
||||
|
||||
# Warning: these default values must be aligned with the values provided
|
||||
# in 'packages/cos-config/cos-config', provide an environment file using the
|
||||
# --cos-config flag if different values are needed.
|
||||
: "${OEM_LABEL:=COS_OEM}"
|
||||
: "${RECOVERY_LABEL:=COS_RECOVERY}"
|
||||
: "${ACTIVE_LABEL:=COS_ACTIVE}"
|
||||
: "${PASSIVE_LABEL:=COS_PASSIVE}"
|
||||
: "${PERSISTENT_LABEL:=COS_PERSISTENT}"
|
||||
: "${SYSTEM_LABEL:=COS_SYSTEM}"
|
||||
: "${STATE_LABEL:=COS_STATE}"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
sync
|
||||
sync
|
||||
sleep 5
|
||||
sync
|
||||
if [ -n "$EFI" ]; then
|
||||
rm -rf $EFI
|
||||
fi
|
||||
if [ -n "$RECOVERY" ]; then
|
||||
rm -rf $RECOVERY
|
||||
fi
|
||||
if [ -n "$STATEDIR" ]; then
|
||||
rm -rf $STATEDIR
|
||||
fi
|
||||
if [ -n "$TARGET" ]; then
|
||||
umount $TARGET || true
|
||||
umount $LOOP || true
|
||||
rm -rf $TARGET
|
||||
fi
|
||||
if [ -n "$WORKDIR" ]; then
|
||||
rm -rf $WORKDIR
|
||||
fi
|
||||
if [ -n "$DRIVE" ]; then
|
||||
umount $DRIVE || true
|
||||
fi
|
||||
if [ -n "$recovery" ]; then
|
||||
umount $recovery || true
|
||||
fi
|
||||
if [ -n "$state" ]; then
|
||||
umount $state || true
|
||||
fi
|
||||
if [ -n "$efi" ]; then
|
||||
umount $efi || true
|
||||
fi
|
||||
if [ -n "$oem" ]; then
|
||||
umount $oem || true
|
||||
fi
|
||||
losetup -D || true
|
||||
}
|
||||
|
||||
ensure_dir_structure() {
|
||||
local target=$1
|
||||
for mnt in /sys /proc /dev /tmp /boot /usr/local /oem
|
||||
do
|
||||
if [ ! -d "${target}${mnt}" ]; then
|
||||
mkdir -p ${target}${mnt}
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "Usage: $0 [options] image.img"
|
||||
echo ""
|
||||
echo "Example: $0 --cos-config cos-config --model odroid-c2 --docker-image <image> output.img"
|
||||
echo ""
|
||||
echo "Flags:"
|
||||
echo " --cos-config: (optional) Specifies a cos-config file for required environment variables"
|
||||
echo " --config: (optional) Specify a cloud-init config file to embed into the final image"
|
||||
echo " --manifest: (optional) Specify a manifest file to customize efi/grub packages installed into the image"
|
||||
echo " --size: (optional) Image size (MB)"
|
||||
echo " --state-partition-size: (optional) Size of the state partition (MB)"
|
||||
echo " --recovery-partition-size: (optional) Size of the recovery partition (MB)"
|
||||
echo " --images-size: (optional) Size of the active/passive/recovery images (MB)"
|
||||
echo " --docker-image: (optional) A container image which will be used for active/passive/recovery system"
|
||||
echo " --local: (optional) Use local repository when building"
|
||||
echo " --directory: (optional) A directory which will be used for active/passive/recovery system"
|
||||
echo " --model: (optional) The board model"
|
||||
echo " --efi-dir: (optional) A directory with files which will be added to the efi partition"
|
||||
exit 1
|
||||
}
|
||||
|
||||
get_url()
|
||||
{
|
||||
FROM=$1
|
||||
TO=$2
|
||||
case $FROM in
|
||||
ftp*|http*|tftp*)
|
||||
n=0
|
||||
attempts=5
|
||||
until [ "$n" -ge "$attempts" ]
|
||||
do
|
||||
curl -o $TO -fL ${FROM} && break
|
||||
n=$((n+1))
|
||||
echo "Failed to download, retry attempt ${n} out of ${attempts}"
|
||||
sleep 2
|
||||
done
|
||||
;;
|
||||
*)
|
||||
cp -f $FROM $TO
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
trap "cleanup" 1 2 3 6 9 14 15 EXIT
|
||||
|
||||
load_vars
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case $1 in
|
||||
--cos-config)
|
||||
shift 1
|
||||
cos_config=$1
|
||||
;;
|
||||
--config)
|
||||
shift 1
|
||||
config=$1
|
||||
;;
|
||||
--manifest)
|
||||
shift 1
|
||||
manifest=$1
|
||||
;;
|
||||
--size)
|
||||
shift 1
|
||||
size=$1
|
||||
;;
|
||||
--local)
|
||||
local_build=true
|
||||
;;
|
||||
--state-partition-size)
|
||||
shift 1
|
||||
state_size=$1
|
||||
;;
|
||||
--recovery-partition-size)
|
||||
shift 1
|
||||
recovery_size=$1
|
||||
;;
|
||||
--images-size)
|
||||
shift 1
|
||||
default_active_size=$1
|
||||
;;
|
||||
--docker-image)
|
||||
shift 1
|
||||
CONTAINER_IMAGE=$1
|
||||
;;
|
||||
--directory)
|
||||
shift 1
|
||||
directory=$1
|
||||
;;
|
||||
--model)
|
||||
shift 1
|
||||
model=$1
|
||||
;;
|
||||
--efi-dir)
|
||||
shift 1
|
||||
efi_dir=$1
|
||||
;;
|
||||
--final-repo)
|
||||
shift 1
|
||||
final_repo=$1
|
||||
;;
|
||||
--repo-type)
|
||||
shift 1
|
||||
repo_type=$1
|
||||
;;
|
||||
-h)
|
||||
usage
|
||||
;;
|
||||
--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
if [ "$#" -gt 2 ]; then
|
||||
usage
|
||||
fi
|
||||
output_image=$1
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift 1
|
||||
done
|
||||
|
||||
if [ "$model" == "rpi64" ]; then
|
||||
container_image=${CONTAINER_IMAGE:-quay.io/costoolkit/examples:rpi-latest}
|
||||
else
|
||||
# Odroid C2 image contains kernel-default-extra, might have broader support
|
||||
container_image=${CONTAINER_IMAGE:-quay.io/costoolkit/examples:odroid-c2-latest}
|
||||
fi
|
||||
|
||||
if [ -n "$cos_config"] && [ -e "$cos_config" ]; then
|
||||
source "$cos_config"
|
||||
fi
|
||||
|
||||
if [ -z "$output_image" ]; then
|
||||
echo "No image file specified"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$manifest" ]; then
|
||||
YQ_PACKAGES_COMMAND=(yq e -o=json "$manifest")
|
||||
final_repo=${final_repo:-$(yq e ".raw_disk.$model.repo" "${manifest}")}
|
||||
fi
|
||||
|
||||
echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
echo "Image Size: $size MB."
|
||||
echo "Recovery Partition: $recovery_size."
|
||||
echo "State Partition: $state_size MB."
|
||||
echo "Images size (active/passive/recovery.img): $default_active_size MB."
|
||||
echo "Model: $model"
|
||||
if [ -n "$container_image" ] && [ -z "$directory" ]; then
|
||||
echo "Container image: $container_image"
|
||||
fi
|
||||
if [ -n "$directory" ]; then
|
||||
echo "Root from directory: $directory"
|
||||
fi
|
||||
echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
|
||||
# Temp dir used during build
|
||||
WORKDIR=$(mktemp -d --tmpdir arm-builder.XXXXXXXXXX)
|
||||
#ROOT_DIR=$(git rev-parse --show-toplevel)
|
||||
TARGET=$(mktemp -d --tmpdir arm-builder.XXXXXXXXXX)
|
||||
STATEDIR=$(mktemp -d --tmpdir arm-builder.XXXXXXXXXX)
|
||||
|
||||
|
||||
export WORKDIR
|
||||
|
||||
# Prepare active.img
|
||||
|
||||
echo ">> Preparing active.img"
|
||||
mkdir -p ${STATEDIR}/cOS
|
||||
|
||||
dd if=/dev/zero of=${STATEDIR}/cOS/active.img bs=1M count=$default_active_size
|
||||
|
||||
mkfs.ext2 ${STATEDIR}/cOS/active.img -L ${ACTIVE_LABEL}
|
||||
|
||||
sync
|
||||
|
||||
LOOP=$(losetup --show -f ${STATEDIR}/cOS/active.img)
|
||||
if [ -z "$LOOP" ]; then
|
||||
echo "No device"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mount -t ext2 $LOOP $TARGET
|
||||
|
||||
ensure_dir_structure $TARGET
|
||||
|
||||
# Download the container image
|
||||
if [ -z "$directory" ]; then
|
||||
echo ">>> Downloading container image"
|
||||
elemental pull-image $( (( $local_build == 'true')) && printf %s '--local' ) $container_image $TARGET
|
||||
else
|
||||
echo ">>> Copying files from $directory"
|
||||
rsync -axq --exclude='host' --exclude='mnt' --exclude='proc' --exclude='sys' --exclude='dev' --exclude='tmp' ${directory}/ $TARGET
|
||||
fi
|
||||
|
||||
umount $TARGET
|
||||
sync
|
||||
|
||||
if [ -n "$LOOP" ]; then
|
||||
losetup -d $LOOP
|
||||
fi
|
||||
|
||||
echo ">> Preparing passive.img"
|
||||
cp -rfv ${STATEDIR}/cOS/active.img ${STATEDIR}/cOS/passive.img
|
||||
tune2fs -L ${PASSIVE_LABEL} ${STATEDIR}/cOS/passive.img
|
||||
|
||||
# Preparing recovery
|
||||
echo ">> Preparing recovery.img"
|
||||
RECOVERY=$(mktemp -d --tmpdir arm-builder.XXXXXXXXXX)
|
||||
if [ -z "$RECOVERY" ]; then
|
||||
echo "No recovery directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p ${RECOVERY}/cOS
|
||||
cp -rfv ${STATEDIR}/cOS/active.img ${RECOVERY}/cOS/recovery.img
|
||||
|
||||
tune2fs -L ${SYSTEM_LABEL} ${RECOVERY}/cOS/recovery.img
|
||||
|
||||
# Install real grub config to recovery
|
||||
cp -rfv /arm/grub/config/* $RECOVERY
|
||||
mkdir -p $RECOVERY/grub2
|
||||
cp -rfv /arm/grub/artifacts/* $RECOVERY/grub2
|
||||
|
||||
sync
|
||||
|
||||
# Prepare efi files
|
||||
echo ">> Preparing EFI partition"
|
||||
EFI=$(mktemp -d --tmpdir arm-builder.XXXXXXXXXX)
|
||||
if [ -z "$EFI" ]; then
|
||||
echo "No EFI directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp -rfv /arm/grub/efi/* $EFI
|
||||
if [ -n "$EFI" ] && [ -n "$efi_dir" ]; then
|
||||
echo "Copy $efi_dir to EFI directory"
|
||||
cp -rfv $efi_dir/* $EFI
|
||||
fi
|
||||
|
||||
echo ">> Writing image and partition table"
|
||||
dd if=/dev/zero of="${output_image}" bs=1024000 count="${size}" || exit 1
|
||||
if [ "$model" == "rpi64" ]; then
|
||||
sgdisk -n 1:8192:+96M -c 1:EFI -t 1:0c00 ${output_image}
|
||||
else
|
||||
sgdisk -n 1:8192:+16M -c 1:EFI -t 1:0700 ${output_image}
|
||||
fi
|
||||
sgdisk -n 2:0:+${state_size}M -c 2:state -t 2:8300 ${output_image}
|
||||
sgdisk -n 3:0:+${recovery_size}M -c 3:recovery -t 3:8300 ${output_image}
|
||||
sgdisk -n 4:0:+64M -c 4:persistent -t 4:8300 ${output_image}
|
||||
|
||||
sgdisk -m 1:2:3:4 ${output_image}
|
||||
|
||||
if [ "$model" == "rpi64" ]; then
|
||||
sfdisk --part-type ${output_image} 1 c
|
||||
fi
|
||||
|
||||
# Prepare the image and copy over the files
|
||||
|
||||
export DRIVE=$(losetup -f "${output_image}" --show)
|
||||
if [ -z "${DRIVE}" ]; then
|
||||
echo "Cannot execute losetup for $output_image"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
device=${DRIVE/\/dev\//}
|
||||
|
||||
if [ -z "$device" ]; then
|
||||
echo "No device"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export device="/dev/mapper/${device}"
|
||||
|
||||
partprobe
|
||||
|
||||
kpartx -va $DRIVE
|
||||
|
||||
echo ">> Populating partitions"
|
||||
efi=${device}p1
|
||||
state=${device}p2
|
||||
recovery=${device}p3
|
||||
persistent=${device}p4
|
||||
|
||||
# Create partitions (RECOVERY, STATE, COS_PERSISTENT)
|
||||
mkfs.vfat -F 32 ${efi}
|
||||
fatlabel ${efi} COS_GRUB
|
||||
|
||||
mkfs.ext4 -F -L ${RECOVERY_LABEL} $recovery
|
||||
mkfs.ext4 -F -L ${STATE_LABEL} $state
|
||||
mkfs.ext4 -F -L ${PERSISTENT_LABEL} $persistent
|
||||
|
||||
mkdir $WORKDIR/state
|
||||
mkdir $WORKDIR/recovery
|
||||
mkdir $WORKDIR/efi
|
||||
|
||||
mount $recovery $WORKDIR/recovery
|
||||
mount $state $WORKDIR/state
|
||||
mount $efi $WORKDIR/efi
|
||||
|
||||
mkdir $WORKDIR/persistent
|
||||
mount $persistent $WORKDIR/persistent
|
||||
mkdir -p $WORKDIR/persistent/cloud-config
|
||||
|
||||
cp -rfv /defaults.yaml $WORKDIR/persistent/cloud-config/01_defaults.yaml
|
||||
|
||||
grub2-editenv $WORKDIR/state/grub_oem_env set "default_menu_entry=Kairos"
|
||||
|
||||
# Set a OEM config file if specified
|
||||
if [ -n "$config" ]; then
|
||||
echo ">> Copying $config OEM config file"
|
||||
get_url $config $WORKDIR/persistent/cloud-config/99_custom.yaml
|
||||
fi
|
||||
umount $WORKDIR/persistent
|
||||
|
||||
# Copy over content
|
||||
cp -arf $EFI/* $WORKDIR/efi
|
||||
cp -arf $RECOVERY/* $WORKDIR/recovery
|
||||
cp -arf $STATEDIR/* $WORKDIR/state
|
||||
|
||||
umount $WORKDIR/recovery
|
||||
umount $WORKDIR/state
|
||||
umount $WORKDIR/efi
|
||||
|
||||
sync
|
||||
|
||||
# Flash uboot and vendor-specific bits
|
||||
echo ">> Performing $model specific bits.."
|
||||
/arm/boards/$model.sh ${DRIVE}
|
||||
|
||||
kpartx -dv $DRIVE
|
||||
|
||||
umount $DRIVE || true
|
||||
|
||||
echo ">> Done writing $output_image"
|
||||
|
||||
echo ">> Creating SHA256 sum"
|
||||
|
||||
sha256sum $output_image > $output_image.sha256
|
||||
|
||||
cleanup
|
||||
8
tools-image/defaults.yaml
Normal file
8
tools-image/defaults.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
name: "Default user"
|
||||
stages:
|
||||
initramfs:
|
||||
- name: "Set default user/pass"
|
||||
users:
|
||||
kairos:
|
||||
passwd: "kairos"
|
||||
15
tools-image/gce.sh
Executable file
15
tools-image/gce.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Transform a raw image disk to gce compatible
|
||||
RAWIMAGE="$1"
|
||||
OUT="${2:-$RAWIMAGE.gce.raw}"
|
||||
cp -rf $RAWIMAGE $OUT
|
||||
|
||||
GB=$((1024*1024*1024))
|
||||
size=$(qemu-img info -f raw --output json "$OUT" | gawk 'match($0, /"virtual-size": ([0-9]+),/, val) {print val[1]}')
|
||||
# shellcheck disable=SC2004
|
||||
ROUNDED_SIZE=$(echo "$size/$GB+1"|bc)
|
||||
echo "Resizing raw image from \"$size\"MB to \"$ROUNDED_SIZE\"GB"
|
||||
qemu-img resize -f raw "$OUT" "$ROUNDED_SIZE"G
|
||||
echo "Compressing raw image $OUT to $OUT.tar.gz"
|
||||
tar -c -z --format=oldgnu -f "$OUT".tar.gz $OUT
|
||||
25
tools-image/netboot.sh
Executable file
25
tools-image/netboot.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# Extracts squashfs, kernel, initrd and generates a ipxe template script
|
||||
|
||||
ISO=$1
|
||||
OUTPUT_NAME=$2
|
||||
ARTIFACT_NAME=$(basename $OUTPUT_NAME)
|
||||
|
||||
isoinfo -x /rootfs.squashfs -R -i $ISO > $OUTPUT_NAME.squashfs
|
||||
isoinfo -x /boot/kernel -R -i $ISO > $OUTPUT_NAME-kernel
|
||||
isoinfo -x /boot/initrd -R -i $ISO > $OUTPUT_NAME-initrd
|
||||
|
||||
URL=${URL:-https://github.com/kairos-io/kairos/releases/download}
|
||||
|
||||
cat > $OUTPUT_NAME.ipxe << EOF
|
||||
#!ipxe
|
||||
set url ${URL}/
|
||||
set kernel $ARTIFACT_NAME-kernel
|
||||
set initrd $ARTIFACT_NAME-initrd
|
||||
set rootfs $ARTIFACT_NAME.squashfs
|
||||
# set config https://example.com/machine-config
|
||||
# set cmdline extra.values=1
|
||||
kernel \${url}/\${kernel} initrd=\${initrd} ip=dhcp rd.cos.disable root=live:\${url}/\${rootfs} netboot nodepair.enable config_url=\${config} console=tty1 console=ttyS0 \${cmdline}
|
||||
initrd \${url}/\${initrd}
|
||||
boot
|
||||
EOF
|
||||
79
tools-image/raw-images.sh
Executable file
79
tools-image/raw-images.sh
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
# Generates EFI bootable images (statically)
|
||||
# This is a re-adaptation of https://github.com/rancher/elemental-toolkit/blob/v0.8.10-1/images/img-builder.sh, which was dropped
|
||||
# How to use:
|
||||
# First extract the image which you want to create an image from:
|
||||
### luet util unpack <image> rootfs
|
||||
# Then convert it to a raw disk (EFI only):
|
||||
### docker run -v $PWD:/output --entrypoint /raw-images.sh -ti --rm test-image /output/rootfs /output/foo.raw cloud-init.yaml
|
||||
|
||||
: "${OEM_LABEL:=COS_OEM}"
|
||||
: "${RECOVERY_LABEL:=COS_RECOVERY}"
|
||||
: "${EXTEND:=}"
|
||||
|
||||
DIRECTORY=$1
|
||||
OUT=${2:-disk.raw}
|
||||
CONFIG=$3
|
||||
|
||||
echo "Output: $OUT"
|
||||
|
||||
set -e
|
||||
|
||||
mkdir -p /build/root/grub2
|
||||
mkdir /build/root/cOS
|
||||
mkdir /build/efi
|
||||
|
||||
cp -rf /raw/grub/* /build/efi
|
||||
cp -rf /raw/grubconfig/* /build/root
|
||||
cp -rf /raw/grubartifacts/* /build/root/grub2
|
||||
|
||||
echo "Generating squashfs from $DIRECTORY"
|
||||
mksquashfs $DIRECTORY recovery.squashfs -b 1024k -comp xz -Xbcj x86
|
||||
mv recovery.squashfs /build/root/cOS/recovery.squashfs
|
||||
|
||||
grub2-editenv /build/root/grub_oem_env set "default_menu_entry=Kairos"
|
||||
|
||||
# Create a 2GB filesystem for RECOVERY including the contents for root (grub config and squasfs container)
|
||||
truncate -s $((2048*1024*1024)) rootfs.part
|
||||
mkfs.ext2 -L "${RECOVERY_LABEL}" -d /build/root rootfs.part
|
||||
|
||||
# Create the EFI partition FAT16 and include the EFI image and a basic grub.cfg
|
||||
truncate -s $((20*1024*1024)) efi.part
|
||||
|
||||
mkfs.fat -F16 -n COS_GRUB efi.part
|
||||
mcopy -s -i efi.part /build/efi/EFI ::EFI
|
||||
|
||||
# Create the grubenv forcing first boot to be on recovery system
|
||||
mkdir -p /build/oem
|
||||
cp /build/root/etc/cos/grubenv_firstboot /build/oem/grubenv
|
||||
if [ -n "$CONFIG" ]; then
|
||||
echo "Copying config file ($CONFIG)"
|
||||
cp $CONFIG /build/oem
|
||||
fi
|
||||
|
||||
# Create a 64MB filesystem for OEM volume
|
||||
truncate -s $((64*1024*1024)) oem.part
|
||||
mkfs.ext2 -L "${OEM_LABEL}" -d /build/oem oem.part
|
||||
|
||||
echo "Generating image $OUT"
|
||||
# Create disk image, add 3MB of initial free space to disk, 1MB is for proper alignement, 2MB are for the hybrid legacy boot.
|
||||
truncate -s $((3*1024*1024)) $OUT
|
||||
{
|
||||
cat efi.part
|
||||
cat oem.part
|
||||
cat rootfs.part
|
||||
} >> $OUT
|
||||
|
||||
# Add an extra MB at the end of the disk for the gpt headers, in fact 34 sectors would be enough, but adding some more does not hurt.
|
||||
truncate -s "+$((1024*1024))" $OUT
|
||||
|
||||
if [ -n "$EXTEND" ]; then
|
||||
echo "Extending image of $EXTEND MB"
|
||||
truncate -s "+$(($EXTEND*1024*1024))" $OUT
|
||||
fi
|
||||
|
||||
# Create the partition table in $OUT (assumes sectors of 512 bytes)
|
||||
sgdisk -n 1:2048:+2M -c 1:legacy -t 1:EF02 $OUT
|
||||
sgdisk -n 2:0:+20M -c 2:UEFI -t 2:EF00 $OUT
|
||||
sgdisk -n 3:0:+64M -c 3:oem -t 3:8300 $OUT
|
||||
sgdisk -n 4:0:+2048M -c 4:root -t 4:8300 $OUT
|
||||
Reference in New Issue
Block a user