Skip to content

Commit

Permalink
Add support for expand disk
Browse files Browse the repository at this point in the history
  • Loading branch information
haijianyang committed Mar 7, 2024
1 parent 3036c0f commit 62b617c
Show file tree
Hide file tree
Showing 39 changed files with 2,705 additions and 43 deletions.
57 changes: 57 additions & 0 deletions api/v1alpha1/common_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
Copyright 2023.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
corev1 "k8s.io/api/core/v1"
)

type Phase string

// xx.
const (
PhaseInitializing = Phase("Initializing")
PhaseProcessing = Phase("Processing")
PhaseSucceeded = Phase("Succeeded")
PhaseFailed = Phase("Failed")
)

type Ansible struct {
// RemotePlaybook 在远端的 playbook,单个 .tar.gz 压缩包,内容可以是单个 yaml 文件,也可以符合 ansible 要求的目录
RemotePlaybook *RemotePlaybook `json:"remotePlaybook,omitempty"`
// LocalPlaybook 本地的 playbook,单个 yaml 文件, secret 引用或者 yaml 字符串
LocalPlaybook *YAMLText `json:"localPlaybook,omitempty"`
// Values 执行 playbook 的参数,yaml 格式,可以是 secret 引用或者 yaml 字符串
Values *YAMLText `json:"values,omitempty"`
}

type RemotePlaybook struct {
// URL playbook 在远端的地址,支持 https
URL string `json:"url"`
// Name 要执行的 playbook 文件名,相对于压缩包顶层的位置
Name string `json:"name"`
// MD5sum 压缩包的 MD5,填写了会进行校验,已经下载过的 playbook 校验通过后跳过重复下载
MD5sum string `json:"md5sum,omitempty"`
}

type YAMLText struct {
// SecretRef specifies the secret which stores yaml text.
SecretRef *corev1.SecretReference `json:"secretRef,omitempty"`
// Content is the inline yaml text.
//+kubebuilder:validation:Format=yaml
Content string `json:"content,omitempty"`
}
21 changes: 21 additions & 0 deletions api/v1alpha1/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
Copyright 2023
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.
*/

// +k8s:deepcopy-gen=package,register
// +k8s:defaulter-gen=TypeMeta
// +groupName=kubesmart.smtx.io

package v1alpha1
36 changes: 36 additions & 0 deletions api/v1alpha1/groupversion_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Copyright 2023.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package v1alpha1 contains API Schema definitions for the kubesmart v1alpha1 API group
// +kubebuilder:object:generate=true
// +groupName=kubesmart.smtx.io
package v1alpha1

import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)

var (
// GroupVersion is group version used to register these objects.
GroupVersion = schema.GroupVersion{Group: "kubesmart.smtx.io", Version: "v1alpha1"}

// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}

// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
109 changes: 109 additions & 0 deletions api/v1alpha1/hostconfig_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
Copyright 2023.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
"crypto/sha256"
"encoding/hex"
"encoding/json"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
// HostConfigFinalizer is set on PrepareForCreate callback.
HostConfigFinalizer = "hostconfig.kubesmart.smtx.io"

// HostConfigReRunAnnotation 表示重新执行.
HostConfigReRunAnnotation = "hostconfig.kubesmart.smtx.io/re-run"

// HostConfigConfigHashAnnotation 记录 spec.config 哈希值的 annotation,当实际计算的哈希值与记录的不同时,代表配置有变更,需要重新执行.
HostConfigConfigHashAnnotation = "hostconfig.kubesmart.smtx.io/config-hash"

// HostConfigNodeNameLabel 表示属于哪个节点,如果长度长度超过 63,会用哈希值代替.
HostConfigNodeNameLabel = "hostconfig.kubesmart.smtx.io/node-name"
)

type HostConfigSpec struct {
NodeName string `json:"nodeName"`
Config Config `json:"config"`
}

type Config struct {
// Ansible 通过 ansible playbook 完成配置
Ansible *Ansible `json:"ansible,omitempty"`
// Timeout 执行一次配置的超时时间
Timeout metav1.Duration `json:"timeout,omitempty"`
}

type HostConfigStatus struct {
// Phase 当前状态
Phase Phase `json:"phase"`
FailureReason string `json:"failureReason,omitempty"`
FailureMessage string `json:"failureMessage,omitempty"`
// LastExecutionTime 最后执行的时间戳
LastExecutionTime *metav1.Time `json:"lastExecutionTime,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:resource:path=hostconfigs,scope=Namespaced,categories=kubesmart,shortName=hc
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="the current phase of HostConfig"
// +kubebuilder:printcolumn:name="LastExecutionTime",type="string",JSONPath=".status.lastExecutionTime",description="the last execution time"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of HostConfig"

// HostConfig is the Schema for the HostConfig API.
type HostConfig struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec HostConfigSpec `json:"spec,omitempty"`
Status HostConfigStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// HostConfigList contains a list of HostConfig.
type HostConfigList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []HostConfig `json:"items"`
}

func init() {
SchemeBuilder.Register(&HostConfig{}, &HostConfigList{})
}

// IsConfigStable returns true if the hash of current config is equal to the hash in annotation.
func (in *HostConfig) IsConfigStable() bool {
annotationHash := GetAnnotation(in, HostConfigConfigHashAnnotation)
currentHash := CalculateHash(in.Spec.Config)
return annotationHash == currentHash
}

func GetAnnotation(obj metav1.Object, key string) string {
annotations := obj.GetAnnotations()
value, _ := annotations[key] //nolint:gosimple
return value
}

func CalculateHash(data interface{}) string {
b, _ := json.Marshal(data) //nolint:errchkjson
hashDate := sha256.Sum256(b)
return hex.EncodeToString(hashDate[:])
}
83 changes: 83 additions & 0 deletions api/v1alpha1/hostoperationjob_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
Copyright 2023.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
// HostOperationJobFinalizer is set on PrepareForCreate callback.
HostOperationJobFinalizer = "hostoperationjob.kubesmart.smtx.io"

// HostOperationJobReRunAnnotation 表示重新执行.
HostOperationJobReRunAnnotation = "hostoperationjob.kubesmart.smtx.io/re-run"

// HostOperationJobNodeNameLabel 表示属于哪个节点,如果长度长度超过 63,会用哈希值代替。
HostOperationJobNodeNameLabel = "hostoperationjob.kubesmart.smtx.io/node-name"
)

type HostOperationJobSpec struct {
NodeName string `json:"nodeName"`
Operation Operation `json:"operation"`
}

type Operation struct {
// Ansible 通过 ansible playbook 完成操作
Ansible *Ansible `json:"ansible,omitempty"`
// Timeout 执行一次操作的超时时间
Timeout metav1.Duration `json:"timeout,omitempty"`
}

type HostOperationJobStatus struct {
// Phase 当前阶段
Phase Phase `json:"phase"`
FailureReason string `json:"failureReason,omitempty"`
FailureMessage string `json:"failureMessage,omitempty"`
// LastExecutionTime 最后执行的时间戳
LastExecutionTime *metav1.Time `json:"lastExecutionTime,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:resource:path=hostoperationjobs,scope=Namespaced,categories=kubesmart,shortName=hoj
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="the current phase of HostOperationJob"
// +kubebuilder:printcolumn:name="LastExecutionTime",type="string",JSONPath=".status.lastExecutionTime",description="the last execution time"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of HostOperationJob"

// HostOperationJob is the Schema for the HostOperationJob API.
type HostOperationJob struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec HostOperationJobSpec `json:"spec,omitempty"`
Status HostOperationJobStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// HostOperationJobList contains a list of HostOperationJob.
type HostOperationJobList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []HostOperationJob `json:"items"`
}

func init() {
SchemeBuilder.Register(&HostOperationJob{}, &HostOperationJobList{})
}
72 changes: 72 additions & 0 deletions api/v1alpha1/method.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
Copyright 2023.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
"context"

corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
apitypes "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
valuesYamlKey = "values.yaml"
)

func (in *YAMLText) IsEmpty() bool {
if in == nil {
return true
}
if in.SecretRef != nil && in.SecretRef.Name == "" && in.Content == "" {
return true
}
return in.SecretRef == nil && in.Content == ""
}

func (in *YAMLText) getAddonSecretNamespacedName(defaultNamespace string) (apitypes.NamespacedName, bool) {
if in.SecretRef != nil && in.SecretRef.Name != "" {
result := apitypes.NamespacedName{
Namespace: in.SecretRef.Namespace,
Name: in.SecretRef.Name,
}
if result.Namespace == "" {
result.Namespace = defaultNamespace
}
return result, true
}
return apitypes.NamespacedName{}, false
}

// GetValuesYaml 优先使用secret,如果不存在尝试使用yaml inline.
func (in *YAMLText) GetValuesYaml(ctx context.Context, c client.Client, defaultNamespace string) (string, error) {
if in.IsEmpty() {
return "", nil
}
secretKey, ok := in.getAddonSecretNamespacedName(defaultNamespace)
if ok {
var err error
secret := &corev1.Secret{}
if err = c.Get(ctx, secretKey, secret); err != nil && !apierrors.IsNotFound(err) {
return "", err
}
return string(secret.Data[valuesYamlKey]), nil
} else {
return in.Content, nil
}
}
Loading

0 comments on commit 62b617c

Please sign in to comment.