mirror of
https://github.com/kubesphere/kubekey.git
synced 2025-12-25 17:12:50 +00:00
feat: support using os repository to install packages.
Signed-off-by: 24sama <jacksama@foxmail.com>
This commit is contained in:
parent
4a72c6cdc5
commit
6506cc9aee
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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"`
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -21,4 +21,5 @@ const (
|
|||
BinDir = "/usr/local/bin"
|
||||
OptCniBinDir = "/opt/cni/bin"
|
||||
SystemdDir = "/etc/systemd/system"
|
||||
MntDir = "/mnt/kubekey"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -77,4 +77,5 @@ type Directory interface {
|
|||
type Repository interface {
|
||||
Update() error
|
||||
Install(pkg ...string) error
|
||||
Add(path string) error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ spec:
|
|||
roles:
|
||||
- control-plane
|
||||
- worker
|
||||
repository:
|
||||
iso: "none"
|
||||
---
|
||||
# KubeadmConfigTemplate referenced by the MachineDeployment
|
||||
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
|
||||
|
|
|
|||
Loading…
Reference in New Issue