feat: support using os repository to install packages.

Signed-off-by: 24sama <jacksama@foxmail.com>
This commit is contained in:
24sama 2022-09-16 09:39:43 +08:00
parent 4a72c6cdc5
commit 6506cc9aee
57 changed files with 4436 additions and 2518 deletions

View File

@ -41,6 +41,10 @@ resources:
kind: KKMachine
path: github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/api/v1beta1
version: v1beta1
webhooks:
defaulting: true
validation: true
webhookVersion: v1
- api:
crdVersion: v1
namespaced: true
@ -49,6 +53,10 @@ resources:
kind: KKMachineTemplate
path: github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/api/v1beta1
version: v1beta1
webhooks:
defaulting: true
validation: true
webhookVersion: v1
- api:
crdVersion: v1
namespaced: true

View File

@ -20,12 +20,15 @@ package v1beta1
type Component struct {
// ZONE is the zone of the KKCluster where can get the binaries.
// If you have problem to access https://storage.googleapis.com, you can set "zone: cn".
// +optional
ZONE string `json:"zone,omitempty"`
// Host is the host to download the binaries.
// +optional
Host string `json:"host,omitempty"`
// Overrides is a list of components download information that need to be overridden.
// +optional
Overrides []Override `json:"overrides,omitempty"`
}
@ -47,5 +50,6 @@ type Override struct {
Path string `json:"path,omitempty"`
// Checksum is the SHA256 checksum of the binary.
// +optional
Checksum string `json:"checksum,omitempty"`
}

View File

@ -68,6 +68,13 @@ const (
KKInstanceInitOSFailedReason = "InitOSFailed"
)
const (
// KKInstanceRepositoryReadyCondition reports on whether successful to use repository to install packages.
KKInstanceRepositoryReadyCondition clusterv1.ConditionType = "InstanceRepositoryReady"
// KKInstanceRepositoryFailedReason used when the instance couldn't use repository to install packages.
KKInstanceRepositoryFailedReason = "InstanceRepositoryFailed"
)
const (
// KKInstanceBinariesReadyCondition reports on whether successful to download binaries.
KKInstanceBinariesReadyCondition clusterv1.ConditionType = "InstanceBinariesReady"

View File

@ -39,7 +39,6 @@ type KKClusterSpec struct {
ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint"`
// ControlPlaneLoadBalancer is optional configuration for customizing control plane behavior.
// +optional
ControlPlaneLoadBalancer *KKLoadBalancerSpec `json:"controlPlaneLoadBalancer,omitempty"`
// Component is optional configuration for modifying the FTP server
@ -49,7 +48,7 @@ type KKClusterSpec struct {
// Registry represents the cluster image registry used to pull the images.
// +optional
Registry Registry `json:"registry"`
Registry Registry `json:"registry,omitempty"`
}
// Nodes represents the information about the nodes available to the cluster
@ -58,12 +57,33 @@ type Nodes struct {
// +optional
Auth Auth `json:"auth"`
// ContainerManager is the container manager config of all instance. It is a global container manager configuration.
// +optional
ContainerManager ContainerManager `json:"containerManager,omitempty"`
// Instances defines all instance contained in kkcluster.
Instances []KKInstanceSpec `json:"instances"`
Instances []InstanceInfo `json:"instances"`
}
// InstanceInfo defines the information about the instance.
type InstanceInfo struct {
// Name is the host name of the machine.
// +kubebuilder:validation:MinLength=1
Name string `json:"name,omitempty"`
// Address is the IP address of the machine.
Address string `json:"address,omitempty"`
// InternalAddress is the internal IP address of the machine.
InternalAddress string `json:"internalAddress,omitempty"`
// Roles is the role of the machine.
// +optional
Roles []Role `json:"roles,omitempty"`
// Arch is the architecture of the machine. e.g. "amd64", "arm64".
// +optional
Arch string `json:"arch,omitempty"`
// Auth is the SSH authentication information of this machine. It will override the global auth configuration.
// +optional
Auth Auth `json:"auth,omitempty"`
}
// KKLoadBalancerSpec defines the desired state of an KK load balancer.

View File

@ -57,7 +57,6 @@ func (k *KKCluster) Default() {
kkclusterlog.Info("default", "name", k.Name)
defaultAuth(&k.Spec.Nodes.Auth)
defaultContainerManager(&k.Spec)
defaultInstance(&k.Spec)
}
@ -75,30 +74,6 @@ func defaultAuth(auth *Auth) {
}
}
func defaultContainerManager(spec *KKClusterSpec) {
// Direct connection to the user-provided CRI socket
if spec.Nodes.ContainerManager.CRISocket != "" {
return
}
if spec.Nodes.ContainerManager.Type == "" {
spec.Nodes.ContainerManager.Type = ContainerdType
}
switch spec.Nodes.ContainerManager.Type {
case ContainerdType:
if spec.Nodes.ContainerManager.Version == "" {
spec.Nodes.ContainerManager.Version = DefaultContainerdVersion
}
spec.Nodes.ContainerManager.CRISocket = DefaultContainerdCRISocket
case DockerType:
if spec.Nodes.ContainerManager.Version == "" {
spec.Nodes.ContainerManager.Version = DefaultDockerVersion
}
spec.Nodes.ContainerManager.CRISocket = DefaultDockerCRISocket
}
}
func defaultInstance(spec *KKClusterSpec) {
for i := range spec.Nodes.Instances {
instance := &spec.Nodes.Instances[i]

View File

@ -45,7 +45,6 @@ func (r *KKClusterTemplate) Default() {
kkclustertemplatelog.Info("default", "name", r.Name)
defaultAuth(&r.Spec.Template.Spec.Nodes.Auth)
defaultContainerManager(&r.Spec.Template.Spec)
defaultInstance(&r.Spec.Template.Spec)
}

View File

@ -94,6 +94,10 @@ type KKInstanceSpec struct {
// ContainerManager is the container manager config of this machine.
// +optional
ContainerManager ContainerManager `json:"containerManager,omitempty"`
// Repository is the repository config of this machine.
// +optional
Repository *Repository `json:"repository,omitempty"`
}
// KKInstanceStatus defines the observed state of KKInstance

View File

@ -33,6 +33,7 @@ type KKMachineSpec struct {
// ProviderID is the unique identifier as specified by the kubekey provider.
ProviderID *string `json:"providerID,omitempty"`
// InstanceID is the name of the KKInstance.
InstanceID *string `json:"instanceID,omitempty"`
// Roles is the role of the machine.
@ -42,6 +43,10 @@ type KKMachineSpec struct {
// ContainerManager is the container manager config of this machine.
// +optional
ContainerManager ContainerManager `json:"containerManager"`
// Repository is the repository config of this machine.
// +optional
Repository *Repository `json:"repository,omitempty"`
}
// KKMachineStatus defines the observed state of KKMachine

View File

@ -0,0 +1,104 @@
/*
Copyright 2022 The KubeSphere Authors.
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 v1beta1
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)
// log is for logging in this package.
var kkmachinelog = logf.Log.WithName("kkmachine-resource")
func (k *KKMachine) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(k).
Complete()
}
//+kubebuilder:webhook:path=/mutate-infrastructure-cluster-x-k8s-io-v1beta1-kkmachine,mutating=true,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=kkmachines,verbs=create;update,versions=v1beta1,name=default.kkmachine.infrastructure.cluster.x-k8s.io,admissionReviewVersions=v1
var _ webhook.Defaulter = &KKMachine{}
// Default implements webhook.Defaulter so a webhook will be registered for the type
func (k *KKMachine) Default() {
kkmachinelog.Info("default", "name", k.Name)
defaultContainerManager(&k.Spec)
}
func defaultContainerManager(spec *KKMachineSpec) {
// Direct connection to the user-provided CRI socket
if spec.ContainerManager.CRISocket != "" {
return
}
if spec.ContainerManager.Type == "" {
spec.ContainerManager.Type = ContainerdType
}
switch spec.ContainerManager.Type {
case ContainerdType:
if spec.ContainerManager.Version == "" {
spec.ContainerManager.Version = DefaultContainerdVersion
}
spec.ContainerManager.CRISocket = DefaultContainerdCRISocket
case DockerType:
if spec.ContainerManager.Version == "" {
spec.ContainerManager.Version = DefaultDockerVersion
}
spec.ContainerManager.CRISocket = DefaultDockerCRISocket
}
}
//+kubebuilder:webhook:path=/validate-infrastructure-cluster-x-k8s-io-v1beta1-kkmachine,mutating=false,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=kkmachines,verbs=create;update,versions=v1beta1,name=validation.kkmachine.infrastructure.cluster.x-k8s.io,admissionReviewVersions=v1
var _ webhook.Validator = &KKMachine{}
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (k *KKMachine) ValidateCreate() error {
kkmachinelog.Info("validate create", "name", k.Name)
var allErrs field.ErrorList
allErrs = append(allErrs, validateRepository(k.Spec.Repository)...)
return aggregateObjErrors(k.GroupVersionKind().GroupKind(), k.Name, allErrs)
}
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (k *KKMachine) ValidateUpdate(old runtime.Object) error {
kkmachinelog.Info("validate update", "name", k.Name)
var allErrs field.ErrorList
allErrs = append(allErrs, validateRepository(k.Spec.Repository)...)
return aggregateObjErrors(k.GroupVersionKind().GroupKind(), k.Name, allErrs)
}
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (k *KKMachine) ValidateDelete() error {
kkmachinelog.Info("validate delete", "name", k.Name)
return nil
}
func validateRepository(repo *Repository) field.ErrorList { //nolint:unparam
var allErrs field.ErrorList
if repo == nil {
return allErrs
}
return allErrs
}

View File

@ -0,0 +1,76 @@
/*
Copyright 2022 The KubeSphere Authors.
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 v1beta1
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)
// log is for logging in this package.
var kkmachinetemplatelog = logf.Log.WithName("kkmachinetemplate-resource")
func (k *KKMachineTemplate) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(k).
Complete()
}
//+kubebuilder:webhook:path=/mutate-infrastructure-cluster-x-k8s-io-v1beta1-kkmachinetemplate,mutating=true,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=kkmachinetemplates,verbs=create;update,versions=v1beta1,name=default.kkmachinetemplate.infrastructure.cluster.x-k8s.io,admissionReviewVersions=v1
var _ webhook.Defaulter = &KKMachineTemplate{}
// Default implements webhook.Defaulter so a webhook will be registered for the type
func (k *KKMachineTemplate) Default() {
kkmachinetemplatelog.Info("default", "name", k.Name)
defaultContainerManager(&k.Spec.Template.Spec)
}
//+kubebuilder:webhook:path=/validate-infrastructure-cluster-x-k8s-io-v1beta1-kkmachinetemplate,mutating=false,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=kkmachinetemplates,verbs=create;update,versions=v1beta1,name=validation.kkmachinetemplate.infrastructure.cluster.x-k8s.io,admissionReviewVersions=v1
var _ webhook.Validator = &KKMachineTemplate{}
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (k *KKMachineTemplate) ValidateCreate() error {
kkmachinetemplatelog.Info("validate create", "name", k.Name)
spec := k.Spec.Template.Spec
var allErrs field.ErrorList
allErrs = append(allErrs, validateRepository(spec.Repository)...)
return aggregateObjErrors(k.GroupVersionKind().GroupKind(), k.Name, allErrs)
}
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (k *KKMachineTemplate) ValidateUpdate(old runtime.Object) error {
kkmachinetemplatelog.Info("validate update", "name", k.Name)
spec := k.Spec.Template.Spec
var allErrs field.ErrorList
allErrs = append(allErrs, validateRepository(spec.Repository)...)
return aggregateObjErrors(k.GroupVersionKind().GroupKind(), k.Name, allErrs)
}
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (k *KKMachineTemplate) ValidateDelete() error {
kkmachinetemplatelog.Info("validate delete", "name", k.Name)
return nil
}

View File

@ -0,0 +1,42 @@
/*
Copyright 2022 The KubeSphere Authors.
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 v1beta1
// ISO type
const (
NONE = "none"
AUTO = "auto"
)
// Repository defines the repository of the instance.
type Repository struct {
// ISO specifies the ISO file name. There are 3 options:
// "": empty string means will not install the packages.
// "none": no ISO file will be used. And capkk will use the default repository to install the required packages.
// "auto": capkk will detect the ISO file automatically. Only support Ubuntu/Debian/CentOS.
// "xxx-20.04-debs-amd64.iso": use the specified name to get the ISO file name.
// +optional
ISO string `json:"iso,omitempty"`
// Update will update the repository packages list and cache if it is true.
// +optional
Update bool `json:"update,omitempty"`
// Packages is a list of packages to be installed.
// +optional
Packages []string `json:"packages,omitempty"`
}

View File

@ -88,6 +88,27 @@ func (in *ContainerManager) DeepCopy() *ContainerManager {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *InstanceInfo) DeepCopyInto(out *InstanceInfo) {
*out = *in
if in.Roles != nil {
in, out := &in.Roles, &out.Roles
*out = make([]Role, len(*in))
copy(*out, *in)
}
in.Auth.DeepCopyInto(&out.Auth)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstanceInfo.
func (in *InstanceInfo) DeepCopy() *InstanceInfo {
if in == nil {
return nil
}
out := new(InstanceInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KKCluster) DeepCopyInto(out *KKCluster) {
*out = *in
@ -367,6 +388,11 @@ func (in *KKInstanceSpec) DeepCopyInto(out *KKInstanceSpec) {
}
in.Auth.DeepCopyInto(&out.Auth)
out.ContainerManager = in.ContainerManager
if in.Repository != nil {
in, out := &in.Repository, &out.Repository
*out = new(Repository)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KKInstanceSpec.
@ -504,6 +530,11 @@ func (in *KKMachineSpec) DeepCopyInto(out *KKMachineSpec) {
copy(*out, *in)
}
out.ContainerManager = in.ContainerManager
if in.Repository != nil {
in, out := &in.Repository, &out.Repository
*out = new(Repository)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KKMachineSpec.
@ -653,10 +684,9 @@ func (in *KKMachineTemplateSpec) DeepCopy() *KKMachineTemplateSpec {
func (in *Nodes) DeepCopyInto(out *Nodes) {
*out = *in
in.Auth.DeepCopyInto(&out.Auth)
out.ContainerManager = in.ContainerManager
if in.Instances != nil {
in, out := &in.Instances, &out.Instances
*out = make([]KKInstanceSpec, len(*in))
*out = make([]InstanceInfo, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
@ -737,3 +767,23 @@ func (in *RegistryAuth) DeepCopy() *RegistryAuth {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Repository) DeepCopyInto(out *Repository) {
*out = *in
if in.Packages != nil {
in, out := &in.Packages, &out.Packages
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Repository.
func (in *Repository) DeepCopy() *Repository {
if in == nil {
return nil
}
out := new(Repository)
in.DeepCopyInto(out)
return out
}

View File

@ -150,25 +150,11 @@ spec:
description: User is the username for SSH authentication.
type: string
type: object
containerManager:
description: ContainerManager is the container manager config
of all instance. It is a global container manager configuration.
properties:
criSocket:
description: CRISocket is used to connect an existing CRIClient.
type: string
type:
description: Type defines the type of ContainerManager. "docker",
"containerd"
type: string
version:
description: Version defines the version of ContainerManager.
type: string
type: object
instances:
description: Instances defines all instance contained in kkcluster.
items:
description: KKInstanceSpec defines the desired state of KKInstance
description: InstanceInfo defines the information about the
instance.
properties:
address:
description: Address is the IP address of the machine.
@ -204,22 +190,6 @@ spec:
description: User is the username for SSH authentication.
type: string
type: object
containerManager:
description: ContainerManager is the container manager config
of this machine.
properties:
criSocket:
description: CRISocket is used to connect an existing
CRIClient.
type: string
type:
description: Type defines the type of ContainerManager.
"docker", "containerd"
type: string
version:
description: Version defines the version of ContainerManager.
type: string
type: object
internalAddress:
description: InternalAddress is the internal IP address
of the machine.

View File

@ -172,29 +172,12 @@ spec:
description: User is the username for SSH authentication.
type: string
type: object
containerManager:
description: ContainerManager is the container manager
config of all instance. It is a global container manager
configuration.
properties:
criSocket:
description: CRISocket is used to connect an existing
CRIClient.
type: string
type:
description: Type defines the type of ContainerManager.
"docker", "containerd"
type: string
version:
description: Version defines the version of ContainerManager.
type: string
type: object
instances:
description: Instances defines all instance contained
in kkcluster.
items:
description: KKInstanceSpec defines the desired state
of KKInstance
description: InstanceInfo defines the information about
the instance.
properties:
address:
description: Address is the IP address of the machine.
@ -232,23 +215,6 @@ spec:
description: User is the username for SSH authentication.
type: string
type: object
containerManager:
description: ContainerManager is the container manager
config of this machine.
properties:
criSocket:
description: CRISocket is used to connect an
existing CRIClient.
type: string
type:
description: Type defines the type of ContainerManager.
"docker", "containerd"
type: string
version:
description: Version defines the version of
ContainerManager.
type: string
type: object
internalAddress:
description: InternalAddress is the internal IP
address of the machine.

View File

@ -103,6 +103,28 @@ spec:
description: Name is the host name of the machine.
minLength: 1
type: string
repository:
description: Repository is the repository config of this machine.
properties:
iso:
description: 'ISO specifies the ISO file name. There are 3 options:
"": empty string means will not install the packages. "none":
no ISO file will be used. And capkk will use the default repository
to install the required packages. "auto": capkk will detect
the ISO file automatically. Only support Ubuntu/Debian/CentOS.
"xxx-20.04-debs-amd64.iso": use the specified name to get the
ISO file name.'
type: string
packages:
description: Packages is a list of packages to be installed.
items:
type: string
type: array
update:
description: Update will update the repository packages list and
cache if it is true.
type: boolean
type: object
roles:
description: Roles is the role of the machine.
items:

View File

@ -72,11 +72,34 @@ spec:
type: string
type: object
instanceID:
description: InstanceID is the name of the KKInstance.
type: string
providerID:
description: ProviderID is the unique identifier as specified by the
kubekey provider.
type: string
repository:
description: Repository is the repository config of this machine.
properties:
iso:
description: 'ISO specifies the ISO file name. There are 3 options:
"": empty string means will not install the packages. "none":
no ISO file will be used. And capkk will use the default repository
to install the required packages. "auto": capkk will detect
the ISO file automatically. Only support Ubuntu/Debian/CentOS.
"xxx-20.04-debs-amd64.iso": use the specified name to get the
ISO file name.'
type: string
packages:
description: Packages is a list of packages to be installed.
items:
type: string
type: array
update:
description: Update will update the repository packages list and
cache if it is true.
type: boolean
type: object
roles:
description: Roles is the role of the machine.
items:

View File

@ -81,11 +81,34 @@ spec:
type: string
type: object
instanceID:
description: InstanceID is the name of the KKInstance.
type: string
providerID:
description: ProviderID is the unique identifier as specified
by the kubekey provider.
type: string
repository:
description: Repository is the repository config of this machine.
properties:
iso:
description: 'ISO specifies the ISO file name. There are
3 options: "": empty string means will not install the
packages. "none": no ISO file will be used. And capkk
will use the default repository to install the required
packages. "auto": capkk will detect the ISO file automatically.
Only support Ubuntu/Debian/CentOS. "xxx-20.04-debs-amd64.iso":
use the specified name to get the ISO file name.'
type: string
packages:
description: Packages is a list of packages to be installed.
items:
type: string
type: array
update:
description: Update will update the repository packages
list and cache if it is true.
type: boolean
type: object
roles:
description: Roles is the role of the machine.
items:

View File

@ -45,6 +45,46 @@ webhooks:
resources:
- kkclustertemplates
sideEffects: None
- admissionReviewVersions:
- v1
clientConfig:
service:
name: webhook-service
namespace: system
path: /mutate-infrastructure-cluster-x-k8s-io-v1beta1-kkmachine
failurePolicy: Fail
name: default.kkmachine.infrastructure.cluster.x-k8s.io
rules:
- apiGroups:
- infrastructure.cluster.x-k8s.io
apiVersions:
- v1beta1
operations:
- CREATE
- UPDATE
resources:
- kkmachines
sideEffects: None
- admissionReviewVersions:
- v1
clientConfig:
service:
name: webhook-service
namespace: system
path: /mutate-infrastructure-cluster-x-k8s-io-v1beta1-kkmachinetemplate
failurePolicy: Fail
name: default.kkmachinetemplate.infrastructure.cluster.x-k8s.io
rules:
- apiGroups:
- infrastructure.cluster.x-k8s.io
apiVersions:
- v1beta1
operations:
- CREATE
- UPDATE
resources:
- kkmachinetemplates
sideEffects: None
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
@ -92,3 +132,43 @@ webhooks:
resources:
- kkclustertemplates
sideEffects: None
- admissionReviewVersions:
- v1
clientConfig:
service:
name: webhook-service
namespace: system
path: /validate-infrastructure-cluster-x-k8s-io-v1beta1-kkmachine
failurePolicy: Fail
name: validation.kkmachine.infrastructure.cluster.x-k8s.io
rules:
- apiGroups:
- infrastructure.cluster.x-k8s.io
apiVersions:
- v1beta1
operations:
- CREATE
- UPDATE
resources:
- kkmachines
sideEffects: None
- admissionReviewVersions:
- v1
clientConfig:
service:
name: webhook-service
namespace: system
path: /validate-infrastructure-cluster-x-k8s-io-v1beta1-kkmachinetemplate
failurePolicy: Fail
name: validation.kkmachinetemplate.infrastructure.cluster.x-k8s.io
rules:
- apiGroups:
- infrastructure.cluster.x-k8s.io
apiVersions:
- v1beta1
operations:
- CREATE
- UPDATE
resources:
- kkmachinetemplates
sideEffects: None

View File

@ -52,10 +52,6 @@ func (r *KKMachineReconciler) createInstance(ctx context.Context, machineScope *
return nil, err
}
if instanceSpec.Arch == "" {
instanceSpec.Arch = "amd64"
}
// todo: if it needs to append a random suffix to the name string
instanceID := instanceSpec.Name
@ -117,10 +113,9 @@ func (r *KKMachineReconciler) getUnassignedInstanceSpec(machineScope *scope.Mach
if err := mergo.Merge(&spec.Auth, auth); err != nil {
return nil, err
}
cm := kkInstanceScope.GlobalContainerManager().DeepCopy()
if err := mergo.Merge(&spec.ContainerManager, cm); err != nil {
return nil, err
}
spec.ContainerManager = *machineScope.KKMachine.Spec.ContainerManager.DeepCopy()
spec.Repository = machineScope.KKMachine.Spec.Repository.DeepCopy()
return &spec, nil
}
return nil, errors.New("unassigned instance not found")

View File

@ -52,6 +52,7 @@ import (
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/bootstrap"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/containermanager"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/provisioning"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/repository"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/util"
)
@ -68,6 +69,7 @@ type KKInstanceReconciler struct {
Recorder record.EventRecorder
sshClientFactory func(scope *scope.InstanceScope) ssh.Interface
bootstrapFactory func(sshClient ssh.Interface, scope scope.LBScope, instanceScope *scope.InstanceScope) service.Bootstrap
repositoryFactory func(sshClient ssh.Interface, scope scope.KKInstanceScope, instanceScope *scope.InstanceScope) service.Repository
binaryFactory func(sshClient ssh.Interface, scope scope.KKInstanceScope, instanceScope *scope.InstanceScope) service.BinaryService
containerManagerFactory func(sshClient ssh.Interface, scope scope.KKInstanceScope, instanceScope *scope.InstanceScope) service.ContainerManager
provisioningFactory func(sshClient ssh.Interface, format bootstrapv1.Format) service.Provisioning
@ -92,6 +94,13 @@ func (r *KKInstanceReconciler) getBootstrapService(sshClient ssh.Interface, scop
return bootstrap.NewService(sshClient, scope, instanceScope)
}
func (r *KKInstanceReconciler) getRepositoryService(sshClient ssh.Interface, scope scope.KKInstanceScope, instanceScope *scope.InstanceScope) service.Repository {
if r.repositoryFactory != nil {
return r.repositoryFactory(sshClient, scope, instanceScope)
}
return repository.NewService(sshClient, scope, instanceScope)
}
func (r *KKInstanceReconciler) getBinaryService(sshClient ssh.Interface, scope scope.KKInstanceScope, instanceScope *scope.InstanceScope) service.BinaryService {
if r.binaryFactory != nil {
return r.binaryFactory(sshClient, scope, instanceScope)
@ -323,6 +332,7 @@ func (r *KKInstanceReconciler) reconcileNormal(ctx context.Context, instanceScop
phases := []func(context.Context, ssh.Interface, *scope.InstanceScope, scope.KKInstanceScope, scope.LBScope) error{
r.reconcileBootstrap,
r.reconcileRepository,
r.reconcileBinaryService,
r.reconcileContainerManager,
r.reconcileProvisioning,

View File

@ -29,6 +29,7 @@ import (
infrav1 "github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/api/v1beta1"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/clients/ssh"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/scope"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service"
)
func (r *KKInstanceReconciler) reconcilePing(_ context.Context, instanceScope *scope.InstanceScope) error {
@ -119,7 +120,48 @@ func (r *KKInstanceReconciler) reconcileBootstrap(_ context.Context, sshClient s
if err := svc.ExecInitScript(); err != nil {
return err
}
if err := svc.Repository(); err != nil {
return nil
}
func (r *KKInstanceReconciler) reconcileRepository(_ context.Context, sshClient ssh.Interface, instanceScope *scope.InstanceScope,
scope scope.KKInstanceScope, _ scope.LBScope) (err error) {
defer func() {
if err != nil {
conditions.MarkFalse(
instanceScope.KKInstance,
infrav1.KKInstanceRepositoryReadyCondition,
infrav1.KKInstanceRepositoryFailedReason,
clusterv1.ConditionSeverityWarning,
err.Error(),
)
} else {
conditions.MarkTrue(instanceScope.KKInstance, infrav1.KKInstanceRepositoryReadyCondition)
}
}()
if conditions.IsTrue(instanceScope.KKInstance, infrav1.KKInstanceRepositoryReadyCondition) {
instanceScope.Info("Instance has been repository ready")
return nil
}
instanceScope.Info("Reconcile repository")
svc := r.getRepositoryService(sshClient, scope, instanceScope)
if err = svc.Check(); err != nil {
return err
}
if err = svc.Get(r.WaitKKInstanceTimeout); err != nil {
return err
}
err = svc.MountISO()
defer func(svc service.Repository) {
_ = svc.UmountISO()
}(svc)
if err != nil {
return err
}
if err = svc.UpdateAndInstall(); err != nil {
return err
}
return nil

View File

@ -9,11 +9,11 @@ replace (
require (
github.com/deckarep/golang-set v1.8.0
github.com/dominodatalab/os-release v0.0.0-20190522011736-bcdb4a3e3c2f
github.com/go-logr/logr v1.2.3
github.com/google/go-cmp v0.5.8
github.com/hashicorp/go-getter v1.6.2
github.com/imdario/mergo v0.3.12
github.com/jinzhu/copier v0.3.5
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.20.0
github.com/pkg/errors v0.9.1

View File

@ -185,8 +185,6 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dominodatalab/os-release v0.0.0-20190522011736-bcdb4a3e3c2f h1:oEt43goQgsL1DzoOyQ/UZHQw7t9TqwyJec9W0vh0wfE=
github.com/dominodatalab/os-release v0.0.0-20190522011736-bcdb4a3e3c2f/go.mod h1:RU3x9VqPvzbOGJ3wtP0pPBtUOp4yU/yzA/8qdxgi/6Q=
github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 h1:7QPwrLT79GlD5sizHf27aoY2RTvw62mO6x7mxkScNk0=
github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46/go.mod h1:esf2rsHFNlZlxsqsZDojNBcnNs5REqIvRrWRHqX0vEU=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@ -413,6 +411,8 @@ github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=

View File

@ -155,6 +155,14 @@ func main() {
setupLog.Error(err, "unable to create webhook", "webhook", "KKClusterTemplate")
os.Exit(1)
}
if err = (&infrav1.KKMachine{}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "KKMachine")
os.Exit(1)
}
if err = (&infrav1.KKMachineTemplate{}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "KKMachineTemplate")
os.Exit(1)
}
//+kubebuilder:scaffold:builder
if err := mgr.AddHealthzCheck("webhook", mgr.GetWebhookServer().StartedChecker()); err != nil {

View File

@ -20,6 +20,7 @@ import (
"context"
"github.com/go-logr/logr"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2/klogr"
@ -126,23 +127,25 @@ func (s *ClusterScope) GlobalAuth() *infrav1.Auth {
return &s.KKCluster.Spec.Nodes.Auth
}
// GlobalContainerManager returns the global container manager spec.
func (s *ClusterScope) GlobalContainerManager() *infrav1.ContainerManager {
return &s.KKCluster.Spec.Nodes.ContainerManager
}
// AllInstancesSpec returns the all KKInstance specs.
func (s *ClusterScope) AllInstancesSpec() []infrav1.KKInstanceSpec {
// AllInstancesInfo returns the all instance specs.
func (s *ClusterScope) AllInstancesInfo() []infrav1.InstanceInfo {
return s.KKCluster.Spec.Nodes.Instances
}
// GetInstancesSpecByRole returns the KKInstance spec for the given role.
func (s *ClusterScope) GetInstancesSpecByRole(role infrav1.Role) []infrav1.KKInstanceSpec {
var arr []infrav1.KKInstanceSpec
for _, v := range s.KKCluster.Spec.Nodes.Instances {
for _, r := range v.Roles {
for i := range s.KKCluster.Spec.Nodes.Instances {
instance := s.KKCluster.Spec.Nodes.Instances[i]
for _, r := range instance.Roles {
if r == role {
arr = append(arr, v)
spec := infrav1.KKInstanceSpec{}
err := copier.Copy(&spec, &instance)
if err != nil {
s.Logger.Info("Failed to copy instance spec", "instance", instance, "error", err)
continue
}
arr = append(arr, spec)
}
}
}

View File

@ -129,6 +129,21 @@ func (i *InstanceScope) ContainerManager() *infrav1.ContainerManager {
return &i.KKInstance.Spec.ContainerManager
}
// RepositoryEnabled returns whether the repository is enabled.
func (i *InstanceScope) RepositoryEnabled() bool {
return i.KKInstance.Spec.Repository != nil && i.KKInstance.Spec.Repository.ISO != ""
}
// RepositoryUseISO returns whether the repository uses ISO.
func (i *InstanceScope) RepositoryUseISO() bool {
return i.RepositoryEnabled() && i.KKInstance.Spec.Repository.ISO != infrav1.NONE
}
// Repository returns the repository of the KKInstance.
func (i *InstanceScope) Repository() *infrav1.Repository {
return i.KKInstance.Spec.Repository
}
// KubernetesVersion returns the Kubernetes version of the KKInstance.
func (i *InstanceScope) KubernetesVersion() string {
return *i.Machine.Spec.Version

View File

@ -32,12 +32,8 @@ type KKInstanceScope interface {
ComponentOverrides() []infrav1.Override
// GlobalAuth returns the global auth configuration of all instances.
GlobalAuth() *infrav1.Auth
// GlobalContainerManager returns the global container manager configuration of all instances.
GlobalContainerManager() *infrav1.ContainerManager
// GlobalRegistry returns the global registry configuration of all instances.
GlobalRegistry() *infrav1.Registry
// AllInstancesSpec returns the KKInstanceSpec
AllInstancesSpec() []infrav1.KKInstanceSpec
// GetInstancesSpecByRole returns all instances filtered by role.
GetInstancesSpecByRole(role infrav1.Role) []infrav1.KKInstanceSpec
// AllInstances returns all KKInstance existing in cluster.

View File

@ -30,6 +30,6 @@ type LBScope interface {
ControlPlaneEndpoint() clusterv1.APIEndpoint
// ControlPlaneLoadBalancer returns the KKLoadBalancerSpec
ControlPlaneLoadBalancer() *infrav1.KKLoadBalancerSpec
// AllInstancesSpec returns the KKInstanceSpec
AllInstancesSpec() []infrav1.KKInstanceSpec
// AllInstancesInfo returns the instance info.
AllInstancesInfo() []infrav1.InstanceInfo
}

View File

@ -25,7 +25,7 @@ import (
infrav1 "github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/api/v1beta1"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation/file"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation/file/checksum"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/util"
)
//go:embed templates
@ -65,45 +65,11 @@ func (s *Service) DownloadAll(timeout time.Duration) error {
}
for _, b := range binaries {
if b.RemoteExist() {
if err := b.Chmod("+x"); err != nil {
return err
}
continue
}
s.instanceScope.V(4).Info("download binary", "binary", b.Name(), "version", b.Version(),
"url", b.URL().String())
if !(b.LocalExist() && b.CompareChecksum() == nil) {
// Only the host is an empty string, we can set ip the zone.
// Because the URL path which in the QingStor is not the same as the default.
if host == "" {
b.SetZone(zone)
}
var path, url, checksumStr string
// If the override is match, we will use the override to replace the default.
if override, ok := overrideMap[b.ID()+b.Version()+b.Arch()]; ok {
path = override.Path
url = override.URL
checksumStr = override.Checksum
}
// Always try to set the "host, path, url, checksum".
// If the these vars are empty strings, it will not make any changes.
b.SetHost(host)
b.SetPath(path)
b.SetURL(url)
b.SetChecksum(checksum.NewStringChecksum(checksumStr))
s.instanceScope.V(4).Info("download binary", "binary", b.Name(),
"version", b.Version(), "url", b.URL().String())
if err := b.Get(timeout); err != nil {
return err
}
if err := b.CompareChecksum(); err != nil {
return err
}
}
if err := b.Copy(true); err != nil {
override := overrideMap[b.ID()+b.Version()+b.Arch()]
if err := util.DownloadAndCopy(b, zone, host, override.Path, override.URL, override.Checksum, timeout); err != nil {
return err
}
if err := b.Chmod("+x"); err != nil {

View File

@ -21,10 +21,8 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"text/template"
osrelease "github.com/dominodatalab/os-release"
"github.com/pkg/errors"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation/directory"
@ -112,7 +110,7 @@ func (s *Service) ExecInitScript() error {
lbHost string
)
for _, host := range s.scope.AllInstancesSpec() {
for _, host := range s.scope.AllInstancesInfo() {
if host.Name != "" {
hostsList = append(hostsList, fmt.Sprintf("%s %s.%s %s",
host.InternalAddress,
@ -152,27 +150,6 @@ func (s *Service) ExecInitScript() error {
return nil
}
// Repository updates the linux package manager and installs some tools.
// Ex:
// apt-get update && apt-get install -y socat conntrack ipset ebtables chrony ipvsadm
// yum clean all && yum makecache && yum install -y openssl socat conntrack ipset ebtables chrony ipvsadm
func (s *Service) Repository() error {
output, err := s.sshClient.SudoCmd("cat /etc/os-release")
if err != nil {
return errors.Wrap(err, "failed to get os release")
}
osrData := osrelease.Parse(strings.Replace(output, "\r\n", "\n", -1))
svc := s.getRepositoryService(osrData.ID)
if err := svc.Update(); err != nil {
return errors.Wrap(err, "failed to update os repository")
}
if err := svc.Install(); err != nil {
return errors.Wrap(err, "failed to use the repository to install software")
}
return nil
}
// ResetNetwork resets the network configuration.
func (s *Service) ResetNetwork() error {
networkResetCmds := []string{

View File

@ -25,7 +25,6 @@ import (
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation/directory"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation/file"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation/repository"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation/user"
)
@ -36,10 +35,9 @@ type Service struct {
scope scope.LBScope
instanceScope *scope.InstanceScope
userFactory func(sshClient ssh.Interface, name, desc string) operation.User
directoryFactory func(sshClient ssh.Interface, path string, mode os.FileMode) operation.Directory
templateFactory func(sshClient ssh.Interface, template *template.Template, data file.Data, dst string) (operation.Template, error)
repositoryFactory func(sshClient ssh.Interface, os string) operation.Repository
userFactory func(sshClient ssh.Interface, name, desc string) operation.User
directoryFactory func(sshClient ssh.Interface, path string, mode os.FileMode) operation.Directory
templateFactory func(sshClient ssh.Interface, template *template.Template, data file.Data, dst string) (operation.Template, error)
}
// NewService returns a new service given the remote instance kubekey build-in bootstrap provision client.
@ -71,10 +69,3 @@ func (s *Service) getTemplateService(template *template.Template, data file.Data
}
return file.NewTemplate(s.sshClient, s.scope.RootFs(), template, data, dst)
}
func (s *Service) getRepositoryService(os string) operation.Repository {
if s.repositoryFactory != nil {
return s.repositoryFactory(s.sshClient, os)
}
return repository.NewService(s.sshClient, os)
}

View File

@ -32,7 +32,7 @@ import (
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/scope"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation/file"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation/file/checksum"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/util"
)
// ContainerdService is a ContainerManager service implementation for containerd.
@ -120,10 +120,11 @@ func (s *ContainerdService) Get(timeout time.Duration) error {
if err != nil {
return err
}
crictl, err := s.getCrictlService(s.sshClient, getFirstMajorVersion(s.instanceScope.KubernetesVersion()), s.instanceScope.Arch())
crictl, err := s.getCrictlService(s.sshClient, getFirstMinorVersion(s.instanceScope.KubernetesVersion()), s.instanceScope.Arch())
if err != nil {
return err
}
binaries := []operation.Binary{
containerd,
runc,
@ -138,42 +139,11 @@ func (s *ContainerdService) Get(timeout time.Duration) error {
}
for _, b := range binaries {
if b.RemoteExist() {
continue
}
s.instanceScope.V(4).Info("download binary", "binary", b.Name(), "version", b.Version(),
"url", b.URL().String())
if !(b.LocalExist() && b.CompareChecksum() == nil) {
// Only the host is an empty string, we can set ip the zone.
// Because the URL path which in the QingStor is not the same as the default.
if host == "" {
b.SetZone(zone)
}
var path, url, checksumStr string
// If the override is match, we will use the override to replace the default.
if override, ok := overrideMap[b.ID()+b.Version()+b.Arch()]; ok {
path = override.Path
url = override.URL
checksumStr = override.Checksum
}
// Always try to set the "host, path, url, checksum".
// If the these vars are empty strings, it will not make any changes.
b.SetHost(host)
b.SetPath(path)
b.SetURL(url)
b.SetChecksum(checksum.NewStringChecksum(checksumStr))
s.instanceScope.V(4).Info("download binary", "binary", b.Name(),
"version", b.Version(), "url", b.URL().String())
if err := b.Get(timeout); err != nil {
return err
}
if err := b.CompareChecksum(); err != nil {
return err
}
}
if err := b.Copy(true); err != nil {
override := overrideMap[b.ID()+b.Version()+b.Arch()]
if err := util.DownloadAndCopy(b, zone, host, override.Path, override.URL, override.Checksum, timeout); err != nil {
return err
}
}
@ -399,7 +369,7 @@ func hasFile(files []os.DirEntry, name string) bool {
return false
}
func getFirstMajorVersion(version string) string {
func getFirstMinorVersion(version string) string {
semantic := versionutil.MustParseSemantic(version)
semantic = semantic.WithPatch(0)
return fmt.Sprintf("v%s", semantic.String())

View File

@ -20,7 +20,7 @@ import (
"testing"
)
func Test_getFirstMajorVersion(t *testing.T) {
func Test_getFirstMinorVersion(t *testing.T) {
tests := []struct {
version string
want string
@ -40,7 +40,7 @@ func Test_getFirstMajorVersion(t *testing.T) {
}
for _, tt := range tests {
t.Run("", func(t *testing.T) {
if got := getFirstMajorVersion(tt.version); got != tt.want {
if got := getFirstMinorVersion(tt.version); got != tt.want {
t.Errorf("getFirstMajorVersion() = %v, want %v", got, tt.want)
}
})

View File

@ -30,12 +30,20 @@ type Bootstrap interface {
CreateDirectory() error
ResetTmpDirectory() error
ExecInitScript() error
Repository() error
ResetNetwork() error
RemoveFiles() error
DaemonReload() error
}
// Repository is the interface for repository provision.
type Repository interface {
Check() error
Get(time time.Duration) error
MountISO() error
UmountISO() error
UpdateAndInstall() error
}
// BinaryService is the interface for binary provision.
type BinaryService interface {
DownloadAll(timeout time.Duration) error

View File

@ -21,4 +21,5 @@ const (
BinDir = "/usr/local/bin"
OptCniBinDir = "/opt/cni/bin"
SystemdDir = "/etc/systemd/system"
MntDir = "/mnt/kubekey"
)

View File

@ -18,6 +18,7 @@ package file
import (
"os"
"reflect"
"github.com/pkg/errors"
@ -49,12 +50,15 @@ type Params struct {
// NewFile returns a new File object given a FileParams.
func NewFile(params Params) (*File, error) {
if params.SSHClient == nil {
if reflect.ValueOf(params.SSHClient).IsNil() {
return nil, errors.New("ssh client is required when creating a File")
}
if params.RootFs == nil {
if reflect.ValueOf(params.RootFs).IsNil() {
return nil, errors.New("rootfs is required when creating a File")
}
if params.Name == "" {
return nil, errors.New("name is required when creating a File")
}
if params.Type == "" {
return nil, errors.New("file type is required when creating a File")
}

View File

@ -0,0 +1,100 @@
/*
Copyright 2022 The KubeSphere Authors.
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 file
import (
"fmt"
"path/filepath"
"strings"
infrav1 "github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/api/v1beta1"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/clients/ssh"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/rootfs"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation/file/checksum"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/util/osrelease"
)
// ISO info
const (
ISOName = "%s.iso"
ISOID = "iso"
ISOURLPathTmpl = "/kubesphere/kubekey/releases/download/v2.2.2/%s"
)
// ISO is a Binary for repository ISO file.
type ISO struct {
*Binary
}
// NewISO returns a new repository ISO.
func NewISO(sshClient ssh.Interface, rootFs rootfs.Interface, os *osrelease.Data, arch string, isoName string) (*ISO, error) {
var (
fileName string
urlPath string
)
if isoName == infrav1.AUTO {
fileName = generateISOName(os, arch)
if fileName == "" {
return nil, fmt.Errorf("" +
"can not detect the ISO file automatically, only support Ubuntu/Debian/CentOS. " +
"Please specify the ISO file name in the '.spec.repository.iso' field")
}
urlPath = fmt.Sprintf(ISOURLPathTmpl, fileName)
} else {
fileName = isoName
urlPath = fmt.Sprintf(ISOURLPathTmpl, fileName)
}
file, err := NewFile(Params{
SSHClient: sshClient,
RootFs: rootFs,
Type: FileBinary,
Name: fileName,
LocalFullPath: filepath.Join(rootFs.ClusterRootFsDir(), fileName),
RemoteFullPath: filepath.Join(MntDir, fileName),
})
if err != nil {
return nil, err
}
u := parseURL(DefaultDownloadHost, urlPath)
internal := checksum.NewChecksum(os.ID, os.VersionID, arch)
binary := NewBinary(BinaryParams{
File: file,
ID: os.ID,
Version: os.VersionID,
Arch: arch,
URL: u,
Checksum: internal,
})
return &ISO{binary}, nil
}
func generateISOName(os *osrelease.Data, arch string) string {
var fileName string
switch os.ID {
case osrelease.UbuntuID:
fileName = fmt.Sprintf(ISOName, strings.Join([]string{os.ID, os.VersionID, "debs", arch}, "-"))
case osrelease.DebianID:
fileName = fmt.Sprintf(ISOName, strings.Join([]string{os.ID + os.VersionID, "debs", arch}, "-"))
case osrelease.CentosID:
fileName = fmt.Sprintf(ISOName, strings.Join([]string{os.ID + os.VersionID, "rpms", arch}, "-"))
}
return fileName
}

View File

@ -77,4 +77,5 @@ type Directory interface {
type Repository interface {
Update() error
Install(pkg ...string) error
Add(path string) error
}

View File

@ -17,6 +17,7 @@
package repository
import (
"fmt"
"strings"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/clients/ssh"
@ -27,14 +28,24 @@ type Debian struct {
SSHClient ssh.Interface
}
// NewDeb returns a new Debian repository manager
// NewDeb returns a new Debian repository manager.
func NewDeb(sshClient ssh.Interface) *Debian {
return &Debian{
SSHClient: sshClient,
}
}
// Update updates the repository cache
// Add adds a local repository using the iso file.
func (d *Debian) Add(path string) error {
if _, err := d.SSHClient.SudoCmd(
fmt.Sprintf("echo 'deb [trusted=yes] file://%s /' "+
"| sudo tee /etc/apt/sources.list.d/kubekey.list > /dev/null", path)); err != nil {
return err
}
return nil
}
// Update updates the repository cache.
func (d *Debian) Update() error {
if _, err := d.SSHClient.Cmd("sudo apt-get update"); err != nil {
return err
@ -42,7 +53,7 @@ func (d *Debian) Update() error {
return nil
}
// Install installs common packages
// Install installs common packages.
func (d *Debian) Install(pkg ...string) error {
if len(pkg) == 0 {
pkg = []string{"socat", "conntrack", "ipset", "ebtables", "chrony", "ipvsadm"}

View File

@ -17,6 +17,7 @@
package repository
import (
"fmt"
"strings"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/clients/ssh"
@ -34,6 +35,23 @@ func NewRPM(sshClient ssh.Interface) *RedhatPackageManager {
}
}
// Add adds a local repository using the iso file.
func (r *RedhatPackageManager) Add(path string) error {
content := fmt.Sprintf(`cat << EOF > /etc/yum.repos.d/kubekey.repo
[base-local]
name=KubeKey-local
baseurl=file://%s
enabled=1
gpgcheck=0
EOF
`, path)
if _, err := r.SSHClient.SudoCmd(content); err != nil {
return err
}
return nil
}
// Update updates the repository cache.
func (r *RedhatPackageManager) Update() error {
if _, err := r.SSHClient.SudoCmd("yum clean all && yum makecache"); err != nil {

View File

@ -17,24 +17,28 @@
package repository
import (
"strings"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/clients/ssh"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/util/osrelease"
)
// Service holds a collection of interfaces.
// The interfaces are broken down like this to group functions together.
type Service interface {
Add(path string) error
Update() error
Install(pkg ...string) error
}
// NewService returns a new service given the remote instance package manager client.
func NewService(sshClient ssh.Interface, os string) Service {
switch strings.ToLower(os) {
case "ubuntu", "debian":
func NewService(sshClient ssh.Interface, os *osrelease.Data) Service {
if os == nil {
return nil
}
switch {
case os.IsLikeDebian():
return NewDeb(sshClient)
case "centos", "rhel", "fedora":
case os.IsLikeFedora():
return NewRPM(sshClient)
default:
return nil

View File

@ -0,0 +1,18 @@
/*
Copyright 2022 The KubeSphere Authors.
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 repository contains the repository service.
package repository

View File

@ -0,0 +1,154 @@
/*
Copyright 2022 The KubeSphere Authors.
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 repository
import (
"fmt"
"path/filepath"
"strings"
"time"
"github.com/pkg/errors"
infrav1 "github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/api/v1beta1"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation/repository"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/util"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/util/osrelease"
)
// Check checks the OS release info.
func (s *Service) Check() error {
if !s.instanceScope.RepositoryEnabled() {
return nil
}
output, err := s.sshClient.SudoCmd("cat /etc/os-release")
if err != nil {
return errors.Wrap(err, "failed to get os release")
}
s.scope.V(4).Info("Get os release", "output", output)
osrData := osrelease.Parse(output)
if osrData == nil || osrData.ID == "" || osrData.VersionID == "" {
return errors.Errorf("failed to parse os release: %s", output)
}
s.os = osrData
return nil
}
// Get gets the binary of ISO file and copy it to the remote instance.
func (s *Service) Get(timeout time.Duration) error {
if !s.instanceScope.RepositoryUseISO() {
return nil
}
iso, err := s.getISOService(s.sshClient, s.os, s.instanceScope.Arch(), s.instanceScope.Repository().ISO)
if err != nil {
return err
}
zone := s.scope.ComponentZone()
host := s.scope.ComponentHost()
overrideMap := make(map[string]infrav1.Override)
for _, o := range s.scope.ComponentOverrides() {
overrideMap[o.ID+o.Version+o.Arch] = o
}
s.instanceScope.V(4).Info("download binary", "binary", iso.Name(), "version", iso.Version(),
"url", iso.URL().String())
override := overrideMap[iso.ID()+iso.Version()+iso.Arch()]
if err := util.DownloadAndCopy(iso, zone, host, override.Path, override.URL, override.Checksum, timeout); err != nil {
return err
}
return nil
}
// MountISO mounts the ISO file to the remote instance.
func (s *Service) MountISO() error {
if !s.instanceScope.RepositoryUseISO() {
return nil
}
iso, err := s.getISOService(s.sshClient, s.os, s.instanceScope.Arch(), s.instanceScope.Repository().ISO)
if err != nil {
return err
}
mountPath := filepath.Join(iso.RemotePath(), "repository")
if _, err := s.sshClient.SudoCmd(fmt.Sprintf("sudo mount -t iso9660 -o loop %s %s", iso.RemotePath(), mountPath)); err != nil {
return errors.Wrapf(err, "mount %s at %s failed", iso.RemotePath(), mountPath)
}
s.mountPath = mountPath
return nil
}
// UmountISO unmounts the ISO file from the remote instance.
func (s *Service) UmountISO() error {
if !s.instanceScope.RepositoryUseISO() {
return nil
}
if _, err := s.sshClient.SudoCmd(fmt.Sprintf("sudo umount %s", s.mountPath)); err != nil {
return errors.Wrapf(err, "umount %s failed", s.mountPath)
}
return nil
}
// UpdateAndInstall updates the linux package manager and installs some tools.
// Ex:
// apt-get update && apt-get install -y socat conntrack ipset ebtables chrony ipvsadm
// yum clean all && yum makecache && yum install -y openssl socat conntrack ipset ebtables chrony ipvsadm
func (s *Service) UpdateAndInstall() error {
if !s.instanceScope.RepositoryEnabled() {
return nil
}
svc := s.getRepositoryService(s.os)
if svc == nil {
checkDeb, debErr := s.sshClient.SudoCmd("which apt")
if debErr == nil && strings.Contains(checkDeb, "bin") {
svc = repository.NewDeb(s.sshClient)
}
checkRpm, rpmErr := s.sshClient.SudoCmd("which yum")
if rpmErr == nil && strings.Contains(checkRpm, "bin") {
svc = repository.NewRPM(s.sshClient)
}
if debErr != nil && rpmErr != nil {
return errors.Errorf("failed to find package manager: %v, %v", debErr, rpmErr)
} else if debErr == nil && rpmErr == nil {
return errors.New("can't detect the main package repository, only one of apt or yum is supported")
}
}
if s.instanceScope.RepositoryUseISO() {
if err := svc.Add(s.mountPath); err != nil {
return errors.Wrapf(err, "failed to add local repository %s", s.mountPath)
}
}
if s.instanceScope.Repository().Update {
if err := svc.Update(); err != nil {
return errors.Wrap(err, "failed to update os repository")
}
}
if err := svc.Install(s.instanceScope.Repository().Packages...); err != nil {
return errors.Wrap(err, "failed to use the repository to install software")
}
return nil
}

View File

@ -0,0 +1,63 @@
/*
Copyright 2022 The KubeSphere Authors.
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 repository
import (
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/clients/ssh"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/scope"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation/file"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation/repository"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/util/osrelease"
)
// Service holds a collection of interfaces.
// The interfaces are broken down like this to group functions together.
type Service struct {
sshClient ssh.Interface
scope scope.KKInstanceScope
instanceScope *scope.InstanceScope
os *osrelease.Data
mountPath string
repositoryFactory func(sshClient ssh.Interface, os *osrelease.Data) operation.Repository
isoFactory func(sshClient ssh.Interface, arch, isoName string) (operation.Binary, error)
}
// NewService returns a new service given the remote instance kubekey build-in repository client.
func NewService(sshClient ssh.Interface, scope scope.KKInstanceScope, instanceScope *scope.InstanceScope) *Service {
return &Service{
sshClient: sshClient,
scope: scope,
instanceScope: instanceScope,
}
}
func (s *Service) getRepositoryService(os *osrelease.Data) operation.Repository {
if s.repositoryFactory != nil {
return s.repositoryFactory(s.sshClient, os)
}
return repository.NewService(s.sshClient, os)
}
func (s *Service) getISOService(sshClient ssh.Interface, os *osrelease.Data, arch string, isoName string) (operation.Binary, error) {
if s.isoFactory != nil {
return s.isoFactory(sshClient, arch, isoName)
}
return file.NewISO(sshClient, s.scope.RootFs(), os, arch, isoName)
}

View File

@ -0,0 +1,18 @@
/*
Copyright 2022 The KubeSphere Authors.
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 util contains utility functions for the service.
package util

View File

@ -0,0 +1,58 @@
/*
Copyright 2022 The KubeSphere Authors.
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 util
import (
"time"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation"
"github.com/kubesphere/kubekey/exp/cluster-api-provider-kubekey/pkg/service/operation/file/checksum"
)
// DownloadAndCopy downloads and copies files to the remote instance.
func DownloadAndCopy(b operation.Binary, zone, host, path, url, checksumStr string, timeout time.Duration) error {
if b.RemoteExist() {
return nil
}
b.SetChecksum(checksum.NewStringChecksum(checksumStr))
if !(b.LocalExist() && b.CompareChecksum() == nil) {
// Only the host is an empty string, we can set up the zone.
// Because the URL path which in the QingStor is not the same as the default.
if host == "" {
b.SetZone(zone)
}
// Always try to set the "host, path, url, checksum".
// If the these vars are empty strings, it will not make any changes.
b.SetHost(host)
b.SetPath(path)
b.SetURL(url)
if err := b.Get(timeout); err != nil {
return err
}
if err := b.CompareChecksum(); err != nil {
return err
}
}
if err := b.Copy(true); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,155 @@
/*
Copyright 2022 The KubeSphere Authors.
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 osrelease is to parse a os release file content.
package osrelease
import (
"bufio"
"bytes"
"strings"
"github.com/pkg/errors"
)
const (
// EtcOsRelease is the path of os-release file.
EtcOsRelease string = "/etc/os-release"
// DebianID is the identifier used by the Debian operating system.
DebianID = "debian"
// FedoraID is the identifier used by the Fedora operating system.
FedoraID = "fedora"
// UbuntuID is the identifier used by the Ubuntu operating system.
UbuntuID = "ubuntu"
// RhelID is the identifier used by the Rhel operating system.
RhelID = "rhel"
// CentosID is the identifier used by the Centos operating system.
CentosID = "centos"
)
// Data exposes the most common identification parameters.
type Data struct {
ID string
IDLike string
Name string
PrettyName string
Version string
VersionID string
}
// Parse is to parse a os release file content.
func Parse(content string) (data *Data) {
data = new(Data)
lines, err := parseString(content)
if err != nil {
return
}
info := make(map[string]string)
for _, v := range lines {
key, value, err := parseLine(v)
if err == nil {
info[key] = value
}
}
data.ID = info["ID"]
data.IDLike = info["ID_LIKE"]
data.Name = info["NAME"]
data.PrettyName = info["PRETTY_NAME"]
data.Version = info["VERSION"]
data.VersionID = info["VERSION_ID"]
return
}
func parseString(content string) (lines []string, err error) {
in := bytes.NewBufferString(content)
reader := bufio.NewReader(in)
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, scanner.Err()
}
func parseLine(line string) (string, string, error) {
// skip empty lines
if line == "" {
return "", "", errors.New("Skipping: zero-length")
}
// skip comments
if line[0] == '#' {
return "", "", errors.New("Skipping: comment")
}
// try to split string at the first '='
splitString := strings.SplitN(line, "=", 2)
if len(splitString) != 2 {
return "", "", errors.New("Can not extract key=value")
}
// trim white space from key and value
key := splitString[0]
key = strings.Trim(key, " ")
value := splitString[1]
value = strings.Trim(value, " ")
// Handle double quotes
if strings.ContainsAny(value, `"`) {
first := value[0:1]
last := value[len(value)-1:]
if first == last && strings.ContainsAny(first, `"'`) {
value = strings.TrimPrefix(value, `'`)
value = strings.TrimPrefix(value, `"`)
value = strings.TrimSuffix(value, `'`)
value = strings.TrimSuffix(value, `"`)
}
}
// expand anything else that could be escaped
value = strings.Replace(value, `\"`, `"`, -1)
value = strings.Replace(value, `\$`, `$`, -1)
value = strings.Replace(value, `\\`, `\`, -1)
value = strings.Replace(value, "\\`", "`", -1)
return key, value, nil
}
// IsLikeDebian will return true for Debian and any other related OS, such as Ubuntu.
func (d *Data) IsLikeDebian() bool {
return d.ID == DebianID || strings.Contains(d.IDLike, DebianID)
}
// IsLikeFedora will return true for Fedora and any other related OS, such as CentOS or RHEL.
func (d *Data) IsLikeFedora() bool {
return d.ID == FedoraID || strings.Contains(d.IDLike, FedoraID)
}
// IsUbuntu will return true for Ubuntu OS.
func (d *Data) IsUbuntu() bool {
return d.ID == UbuntuID
}
// IsRHEL will return true for RHEL OS.
func (d *Data) IsRHEL() bool {
return d.ID == RhelID
}
// IsCentOS will return true for CentOS.
func (d *Data) IsCentOS() bool {
return d.ID == CentosID
}

View File

@ -0,0 +1,107 @@
/*
Copyright 2022 The KubeSphere Authors.
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 osrelease
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"reflect"
"testing"
)
var testResult = []struct {
name string
data *Data
}{
{
name: "ubuntu2204",
data: &Data{
ID: "ubuntu",
IDLike: "debian",
Name: "Ubuntu",
PrettyName: "Ubuntu 22.04 LTS",
Version: "22.04 (Jammy Jellyfish)",
VersionID: "22.04",
},
},
{
name: "ubuntu2004",
data: &Data{
ID: "ubuntu",
IDLike: "debian",
Name: "Ubuntu",
PrettyName: "Ubuntu 20.04.4 LTS",
Version: "20.04.4 LTS (Focal Fossa)",
VersionID: "20.04",
},
},
{
name: "ubuntu1804",
data: &Data{
ID: "ubuntu",
IDLike: "debian",
Name: "Ubuntu",
PrettyName: "Ubuntu 18.04.2 LTS",
Version: "18.04.2 LTS (Bionic Beaver)",
VersionID: "18.04",
},
},
{
name: "centos7",
data: &Data{
ID: "centos",
IDLike: "rhel fedora",
Name: "CentOS Linux",
PrettyName: "CentOS Linux 7 (Core)",
Version: "7 (Core)",
VersionID: "7",
},
},
}
func TestParse(t *testing.T) {
for _, tt := range testResult {
t.Run(tt.name, func(t *testing.T) {
bs, err := os.ReadFile(filepath.Join("test", tt.name))
if err != nil {
t.Errorf("cannot read datafile: %v", err)
}
info := Parse(string(bs))
if !reflect.DeepEqual(tt.data, info) {
t.Errorf("Parse() got = %v, want %v", info, tt.data)
}
})
}
}
func TestParseByCommand(t *testing.T) {
for _, tt := range testResult {
t.Run(tt.name, func(t *testing.T) {
c := fmt.Sprintf("cat %s", filepath.Join("test", tt.name))
bs, err := exec.Command("/bin/bash", "-c", c).Output() //nolint:gosec
if err != nil {
t.Errorf("cannot cat datafile: %v", err)
}
info := Parse(string(bs))
if !reflect.DeepEqual(tt.data, info) {
t.Errorf("Parse() got = %v, want %v", info, tt.data)
}
})
}
}

View File

@ -0,0 +1,15 @@
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"
CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"

View File

@ -0,0 +1,17 @@
NAME="Red Hat Enterprise Linux Server"
VERSION="7.6 (Maipo)"
ID="rhel"
ID_LIKE="fedora"
VARIANT="Server"
VARIANT_ID="server"
VERSION_ID="7.6"
PRETTY_NAME="Red Hat Enterprise Linux 7.6"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:redhat:enterprise_linux:7.6:GA:server"
HOME_URL="https://www.redhat.com/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 7"
REDHAT_BUGZILLA_PRODUCT_VERSION=7.6
REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux"
REDHAT_SUPPORT_PRODUCT_VERSION="7.6"

View File

@ -0,0 +1,12 @@
NAME="Ubuntu"
VERSION="18.04.2 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.2 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

View File

@ -0,0 +1,12 @@
NAME="Ubuntu"
VERSION="20.04.4 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.4 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal

View File

@ -0,0 +1,12 @@
PRETTY_NAME="Ubuntu 22.04 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04 (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

View File

@ -103,8 +103,8 @@ variables:
KKZONE: "cn"
USER_NAME: "ubuntu"
PASSWORD: "Qcloud@123"
INSTANCES: "[{address: 172.31.50.190}, {address: 172.31.50.197}]"
CONTROL_PLANE_ENDPOINT_IP: "172.31.50.252"
INSTANCES: "[{address: 192.168.100.3}, {address: 192.168.100.4}]"
CONTROL_PLANE_ENDPOINT_IP: "192.168.100.100"
intervals:
default/wait-controllers: [ "5m", "10s" ]

File diff suppressed because it is too large Load Diff

View File

@ -49,6 +49,8 @@ spec:
spec:
roles:
- control-plane
repository:
iso: "none"
---
# KubeadmControlPlane referenced by the Cluster object with
# - the label kcp-adoption.step2, because it should be created in the second step of the kcp-adoption test.

View File

@ -9,6 +9,8 @@ spec:
roles:
- control-plane
- worker
repository:
iso: "none"
---
# KubeadmConfigTemplate referenced by the MachineDeployment
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1