add infra module (#754)

* hotfix(master): add infra module
This commit is contained in:
cuisongliu
2021-12-19 18:38:03 +08:00
committed by GitHub
parent 5fde3d32f7
commit 905ceada4d
83 changed files with 2685 additions and 628 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ import (
"strings"
"sync"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/utils/ssh"
+1 -1
View File
@@ -18,7 +18,7 @@ import (
"fmt"
"os"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
install "github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/utils/logger"
+1 -1
View File
@@ -19,7 +19,7 @@ import (
"fmt"
"io/ioutil"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils/logger"
+28 -28
View File
@@ -84,7 +84,7 @@ spec:
listKind: BGPConfigurationList
plural: bgpconfigurations
singular: bgpconfiguration
scope: Cluster
scope: Infra
versions:
- name: v1
schema:
@@ -218,7 +218,7 @@ spec:
listKind: BGPPeerList
plural: bgppeers
singular: bgppeer
scope: Cluster
scope: Infra
versions:
- name: v1
schema:
@@ -325,7 +325,7 @@ spec:
listKind: BlockAffinityList
plural: blockaffinities
singular: blockaffinity
scope: Cluster
scope: Infra
versions:
- name: v1
schema:
@@ -386,7 +386,7 @@ spec:
listKind: ClusterInformationList
plural: clusterinformations
singular: clusterinformation
scope: Cluster
scope: Infra
versions:
- name: v1
schema:
@@ -450,7 +450,7 @@ spec:
listKind: FelixConfigurationList
plural: felixconfigurations
singular: felixconfiguration
scope: Cluster
scope: Infra
versions:
- name: v1
schema:
@@ -1013,7 +1013,7 @@ spec:
listKind: GlobalNetworkPolicyList
plural: globalnetworkpolicies
singular: globalnetworkpolicy
scope: Cluster
scope: Infra
versions:
- name: v1
schema:
@@ -1784,7 +1784,7 @@ spec:
listKind: GlobalNetworkSetList
plural: globalnetworksets
singular: globalnetworkset
scope: Cluster
scope: Infra
versions:
- name: v1
schema:
@@ -1837,7 +1837,7 @@ spec:
listKind: HostEndpointList
plural: hostendpoints
singular: hostendpoint
scope: Cluster
scope: Infra
versions:
- name: v1
schema:
@@ -1945,7 +1945,7 @@ spec:
listKind: IPAMBlockList
plural: ipamblocks
singular: ipamblock
scope: Cluster
scope: Infra
versions:
- name: v1
schema:
@@ -2026,7 +2026,7 @@ spec:
listKind: IPAMConfigList
plural: ipamconfigs
singular: ipamconfig
scope: Cluster
scope: Infra
versions:
- name: v1
schema:
@@ -2082,7 +2082,7 @@ spec:
listKind: IPAMHandleList
plural: ipamhandles
singular: ipamhandle
scope: Cluster
scope: Infra
versions:
- name: v1
schema:
@@ -2138,7 +2138,7 @@ spec:
listKind: IPPoolList
plural: ippools
singular: ippool
scope: Cluster
scope: Infra
versions:
- name: v1
schema:
@@ -2237,7 +2237,7 @@ spec:
listKind: KubeControllersConfigurationList
plural: kubecontrollersconfigurations
singular: kubecontrollersconfiguration
scope: Cluster
scope: Infra
versions:
- name: v1
schema:
@@ -3653,7 +3653,7 @@ spec:
configMapKeyRef:
name: calico-config
key: calico_backend
# Cluster type to identify the deployment type
# Infra type to identify the deployment type
- name: CLUSTER_TYPE
value: "k8s,bgp"
# Auto-detect the BGP IP address.
@@ -3870,7 +3870,7 @@ metadata:
---
# This manifest creates a Pod Disruption Budget for Controller to allow K8s Cluster Autoscaler to evict
# This manifest creates a Pod Disruption Budget for Controller to allow K8s Infra Autoscaler to evict
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
@@ -3941,7 +3941,7 @@ kind: CustomResourceDefinition
metadata:
name: felixconfigurations.crd.projectcalico.org
spec:
scope: Cluster
scope: Infra
group: crd.projectcalico.org
version: v1
names:
@@ -3955,7 +3955,7 @@ kind: CustomResourceDefinition
metadata:
name: ipamblocks.crd.projectcalico.org
spec:
scope: Cluster
scope: Infra
group: crd.projectcalico.org
version: v1
names:
@@ -3970,7 +3970,7 @@ kind: CustomResourceDefinition
metadata:
name: blockaffinities.crd.projectcalico.org
spec:
scope: Cluster
scope: Infra
group: crd.projectcalico.org
version: v1
names:
@@ -3985,7 +3985,7 @@ kind: CustomResourceDefinition
metadata:
name: ipamhandles.crd.projectcalico.org
spec:
scope: Cluster
scope: Infra
group: crd.projectcalico.org
version: v1
names:
@@ -4000,7 +4000,7 @@ kind: CustomResourceDefinition
metadata:
name: ipamconfigs.crd.projectcalico.org
spec:
scope: Cluster
scope: Infra
group: crd.projectcalico.org
version: v1
names:
@@ -4015,7 +4015,7 @@ kind: CustomResourceDefinition
metadata:
name: bgppeers.crd.projectcalico.org
spec:
scope: Cluster
scope: Infra
group: crd.projectcalico.org
version: v1
names:
@@ -4030,7 +4030,7 @@ kind: CustomResourceDefinition
metadata:
name: bgpconfigurations.crd.projectcalico.org
spec:
scope: Cluster
scope: Infra
group: crd.projectcalico.org
version: v1
names:
@@ -4045,7 +4045,7 @@ kind: CustomResourceDefinition
metadata:
name: ippools.crd.projectcalico.org
spec:
scope: Cluster
scope: Infra
group: crd.projectcalico.org
version: v1
names:
@@ -4060,7 +4060,7 @@ kind: CustomResourceDefinition
metadata:
name: hostendpoints.crd.projectcalico.org
spec:
scope: Cluster
scope: Infra
group: crd.projectcalico.org
version: v1
names:
@@ -4075,7 +4075,7 @@ kind: CustomResourceDefinition
metadata:
name: clusterinformations.crd.projectcalico.org
spec:
scope: Cluster
scope: Infra
group: crd.projectcalico.org
version: v1
names:
@@ -4090,7 +4090,7 @@ kind: CustomResourceDefinition
metadata:
name: globalnetworkpolicies.crd.projectcalico.org
spec:
scope: Cluster
scope: Infra
group: crd.projectcalico.org
version: v1
names:
@@ -4105,7 +4105,7 @@ kind: CustomResourceDefinition
metadata:
name: globalnetworksets.crd.projectcalico.org
spec:
scope: Cluster
scope: Infra
group: crd.projectcalico.org
version: v1
names:
@@ -4484,7 +4484,7 @@ spec:
configMapKeyRef:
name: calico-config
key: calico_backend
# Cluster type to identify the deployment type
# Infra type to identify the deployment type
- name: CLUSTER_TYPE
value: "k8s,bgp"
# Auto-detect the BGP IP address.
+3
View File
@@ -0,0 +1,3 @@
# infra模块
根据Clusterfile里的定义去申请IaaS资源,保障IaaS资源与Clusterfile中的定义保持终态一致
+85
View File
@@ -0,0 +1,85 @@
/*
Copyright 2021 cuisongliu@qq.com.
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 aliyun
import (
"time"
v2 "github.com/fanux/sealos/pkg/types/v1beta1"
)
const (
EnvAccessKey = "ECS_AKID"
EnvAccessSecret = "ECS_AKSK"
EnvRegion = "ECS_REGION"
)
const (
Scheme = "https"
IPProtocol = "tcp"
APIServerPortRange = "6443/6443"
SSHPortRange = "22/22"
SourceCidrIP = "0.0.0.0/0"
CidrBlock = "172.16.0.0/24"
Policy = "accept"
DestinationResource = "InstanceType"
InstanceChargeType = "PostPaid"
InternetChargeType = "PayByTraffic"
defaultImageAmdID = "centos_7_9_x64_20G_alibase_20210927.vhd"
defaultImageArmID = "anolisos_7_7_arm64_20G_anck_alibase_20211118.vhd"
Product = "product"
Role = "role"
Master = "master"
Node = "node"
Stopped = "Stopped"
AvailableTypeStatus = "WithStock"
Bandwidth = "100"
AliDomain = "www.sealyun.com/"
DefaultRegionID = "cn-shanghai"
TryTimes = 10
TrySleepTime = time.Second
JustGetInstanceInfo = 0
)
type ResourceName string
const (
EipID ResourceName = AliDomain + "EipID"
VpcID ResourceName = AliDomain + "VpcID"
VSwitchID ResourceName = AliDomain + "VSwitchID"
SecurityGroupID ResourceName = AliDomain + "SecurityGroupID"
SystemInfo ResourceName = AliDomain + "SystemInfo"
ShouldBeDeleteInstancesIDs ResourceName = "ShouldBeDeleteInstancesIDs"
)
func (r ResourceName) Value(status v2.InfraStatus) string {
var value string
switch r {
case EipID:
value = status.EIPID
case VpcID:
value = status.VpcID
case VSwitchID:
value = status.VSwitchID
case SecurityGroupID:
value = status.SecurityGroupID
case ShouldBeDeleteInstancesIDs:
value = status.ShouldBeDeleteInstancesIDs
default:
}
return value
}
+522
View File
@@ -0,0 +1,522 @@
// Copyright © 2021 Alibaba Group Holding Ltd.
//
// 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 aliyun
import (
"errors"
"fmt"
"os"
"strings"
v2 "github.com/fanux/sealos/pkg/types/v1beta1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/utils/logger"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses"
"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
)
type Instance struct {
CPU int
Memory float64
InstanceID string
PrimaryIPAddress string
}
type EcsManager struct {
}
func (a *AliProvider) RetryEcsRequest(request requests.AcsRequest, response responses.AcsResponse) error {
return a.RetryEcsAction(request, response, TryTimes)
}
func (a *AliProvider) RetryEcsAction(request requests.AcsRequest, response responses.AcsResponse, tryTimes int) error {
return utils.Retry(tryTimes, TrySleepTime, func() error {
err := a.EcsClient.DoAction(request, response)
if err != nil {
return err
}
return nil
})
}
func (a *AliProvider) RetryEcsInstanceType(request requests.AcsRequest, response responses.AcsResponse, instances []string) error {
for i := 0; i < len(instances); i++ {
switch req := request.(type) {
case *ecs.ModifyInstanceSpecRequest:
req.InstanceType = instances[i]
case *ecs.RunInstancesRequest:
req.InstanceType = instances[i]
}
err := a.RetryEcsAction(request, response, 4)
if err == nil {
a.Infra.Spec.Instance.Type = instances[i]
logger.Debug("use SystemInfo type: %s", instances[i])
break
} else if i == len(instances)-1 {
return fmt.Errorf("failed to get ecs SystemInfo type, %v", err)
}
}
return nil
}
func (a *AliProvider) TryGetInstance(request *ecs.DescribeInstancesRequest, response *ecs.DescribeInstancesResponse, expectCount int) error {
return utils.Retry(TryTimes, TrySleepTime, func() error {
err := a.EcsClient.DoAction(request, response)
var ipList []string
if err != nil {
return err
}
instances := response.Instances.Instance
if expectCount == -1 {
return nil
}
if len(instances) != expectCount {
return errors.New("the number of instances is not as expected")
}
for _, instance := range instances {
if instance.NetworkInterfaces.NetworkInterface[0].PrimaryIpAddress == "" {
return errors.New("PrimaryIpAddress cannt nob be nil")
}
if len(ipList) != 0 && !utils.NotIn(instance.NetworkInterfaces.NetworkInterface[0].PrimaryIpAddress, ipList) {
return errors.New("PrimaryIpAddress cannt nob be same")
}
ipList = append(ipList, instance.NetworkInterfaces.NetworkInterface[0].PrimaryIpAddress)
}
return nil
})
}
func (a *AliProvider) InputIPlist(instanceRole string) (ipList []string, err error) {
var hosts *v2.Hosts
switch instanceRole {
case Master:
hosts = &a.Infra.Spec.Masters
case Node:
hosts = a.Infra.Spec.Nodes
}
if hosts == nil {
return nil, err
}
instances, err := a.GetInstancesInfo(instanceRole, hosts.Count)
if err != nil {
return nil, err
}
for _, instance := range instances {
ipList = append(ipList, instance.PrimaryIPAddress)
}
return ipList, nil
}
func (a *AliProvider) GetInstanceStatus(instanceID string) (instanceStatus string, err error) {
request := ecs.CreateDescribeInstanceStatusRequest()
request.Scheme = Scheme
request.InstanceId = &[]string{instanceID}
response := ecs.CreateDescribeInstanceStatusResponse()
err = a.RetryEcsRequest(request, response)
if err != nil {
return "", fmt.Errorf("get SystemInfo status failed %v , error :%v", instanceID, err)
}
if len(response.InstanceStatuses.InstanceStatus) == 0 {
return "", fmt.Errorf("SystemInfo list is empty")
}
return response.InstanceStatuses.InstanceStatus[0].Status, nil
}
func (a *AliProvider) PoweroffInstance(instanceID string) error {
request := ecs.CreateStopInstancesRequest()
request.Scheme = Scheme
request.InstanceId = &[]string{instanceID}
response := ecs.CreateStopInstancesResponse()
return a.RetryEcsRequest(request, response)
}
func (a *AliProvider) StartInstance(instanceID string) error {
request := ecs.CreateStartInstanceRequest()
request.Scheme = Scheme
request.InstanceId = instanceID
response := ecs.CreateStartInstanceResponse()
return a.RetryEcsRequest(request, response)
}
func (a *AliProvider) ChangeInstanceType(instanceID, instanceRole string, cpu int, memory float64) error {
instanceStatus, err := a.GetInstanceStatus(instanceID)
if err != nil {
return err
}
if instanceStatus != Stopped {
err = a.PoweroffInstance(instanceID)
if err != nil {
return err
}
}
expectInstanceType, err := a.GetAvailableResource(instanceRole, cpu, memory)
if err != nil {
return err
}
request := ecs.CreateModifyInstanceSpecRequest()
request.Scheme = Scheme
request.InstanceId = instanceID
//_, err = d.Client.ModifyInstanceSpec(request)
response := ecs.CreateModifyInstanceSpecResponse()
err = a.RetryEcsInstanceType(request, response, expectInstanceType)
if err != nil {
return err
}
return a.StartInstance(instanceID)
}
func (a *AliProvider) GetInstancesInfo(instancesRole string, expectCount int) (instances []Instance, err error) {
var count int
tag := make(map[string]string)
tag[Product] = a.Infra.Name
tag[Role] = instancesRole
if expectCount == 0 {
count = -1
} else {
count = expectCount
}
instancesTags := CreateDescribeInstancesTag(tag)
request := ecs.CreateDescribeInstancesRequest()
request.Scheme = Scheme
request.RegionId = a.Infra.Status.RegionID
request.VSwitchId = a.Infra.Status.VSwitchID
request.SecurityGroupId = a.Infra.Status.SecurityGroupID
request.Tag = &instancesTags
response := ecs.CreateDescribeInstancesResponse()
err = a.TryGetInstance(request, response, count)
if err != nil {
return nil, err
}
for _, i := range response.Instances.Instance {
instances = append(instances,
Instance{
CPU: i.Cpu,
Memory: float64(i.Memory / 1024),
InstanceID: i.InstanceId,
PrimaryIPAddress: i.NetworkInterfaces.NetworkInterface[0].PrimaryIpAddress})
}
return
}
func (a *AliProvider) ReconcileInstances(instanceRole string) error {
var hosts *v2.Hosts
var instances []Instance
var instancesIDs string
var IPList []string
switch instanceRole {
case Master:
hosts = &a.Infra.Spec.Masters
instancesIDs = a.Infra.Status.MasterIDs
if hosts.Count == 0 {
return errors.New("master count not set")
}
case Node:
hosts = a.Infra.Spec.Nodes
instancesIDs = a.Infra.Status.NodeIDs
if hosts == nil {
return nil
}
}
if hosts == nil {
return errors.New("hosts not set")
}
var err error
if instancesIDs != "" {
instances, err = a.GetInstancesInfo(instanceRole, JustGetInstanceInfo)
}
if err != nil {
return err
}
if i := hosts.Count; len(instances) < i {
err = a.RunInstances(instanceRole, i-len(instances))
if err != nil {
return err
}
ipList, err := a.InputIPlist(instanceRole)
if err != nil {
return err
}
IPList = utils.AppendIPList(IPList, ipList)
logger.Info("get scale up IP list %v, append iplist %v, host count %s", ipList, IPList, hosts.Count)
}
for _, instance := range instances {
if instance.CPU != hosts.CPU || instance.Memory != hosts.Memory {
err = a.ChangeInstanceType(instance.InstanceID, instanceRole, hosts.CPU, hosts.Memory)
if err != nil {
return err
}
}
}
if instanceRole == Master {
a.Infra.Status.Masters = IPList
} else {
a.Infra.Status.Nodes = IPList
}
logger.Info("reconcile %s instances success %v ", instanceRole, IPList)
return nil
}
func (a *AliProvider) DeleteInstances() error {
instanceIDs := strings.Split(a.Infra.Status.ShouldBeDeleteInstancesIDs, ",")
if len(instanceIDs) == 0 {
return nil
}
request := ecs.CreateDeleteInstancesRequest()
request.Scheme = Scheme
request.InstanceId = &instanceIDs
request.Force = requests.NewBoolean(true)
//_, err := d.Client.DeleteInstances(request)
response := ecs.CreateDeleteInstancesResponse()
err := a.RetryEcsRequest(request, response)
if err != nil {
return err
}
a.Infra.Status.ShouldBeDeleteInstancesIDs = ""
return nil
}
func CreateDescribeInstancesTag(tags map[string]string) (instanceTags []ecs.DescribeInstancesTag) {
for k, v := range tags {
instanceTags = append(instanceTags, ecs.DescribeInstancesTag{Key: k, Value: v})
}
return
}
func CreateInstanceDataDisk(dataDisks []string, category string) (instanceDisks []ecs.RunInstancesDataDisk) {
for _, v := range dataDisks {
instanceDisks = append(instanceDisks,
ecs.RunInstancesDataDisk{Size: v, Category: category})
}
return
}
type InstanceInfo struct {
InstanceType string
ZoneID string
}
func (a *AliProvider) GetAvailableResourcesForSystem() (instances *InstanceInfo, err error) {
request := ecs.CreateDescribeAvailableResourceRequest()
request.Scheme = Scheme
if a.Infra.Status.ZoneID != "" {
request.ZoneId = a.Infra.Status.ZoneID
}
if a.Infra.Spec.Instance.Type != "" {
request.InstanceType = a.Infra.Spec.Instance.Type
}
request.RegionId = a.Infra.Status.RegionID
request.DestinationResource = DestinationResource
request.InstanceChargeType = InstanceChargeType
request.SystemDiskCategory = a.Infra.Spec.Instance.SystemCategory
request.DataDiskCategory = a.Infra.Spec.Instance.DataCategory
request.SpotStrategy = a.Infra.Status.SpotStrategy
response := ecs.CreateDescribeAvailableResourceResponse()
err = a.EcsClient.DoAction(request, response)
if err != nil {
return nil, err
}
if len(response.AvailableZones.AvailableZone) < 1 {
return nil, fmt.Errorf("resources not find")
}
for _, i := range response.AvailableZones.AvailableZone {
for _, f := range i.AvailableResources.AvailableResource {
for _, r := range f.SupportedResources.SupportedResource {
if r.StatusCategory == AvailableTypeStatus {
return &InstanceInfo{
InstanceType: r.Value,
ZoneID: i.ZoneId,
}, nil
}
}
}
}
return
}
func (a *AliProvider) GetAvailableResource(instanceRole string, cores int, memory float64) (instanceType []string, err error) {
request := ecs.CreateDescribeAvailableResourceRequest()
request.Scheme = Scheme
request.RegionId = a.Infra.Status.RegionID
request.ZoneId = a.Infra.Status.ZoneID
request.DestinationResource = DestinationResource
request.InstanceChargeType = InstanceChargeType
request.Cores = requests.NewInteger(cores)
request.Memory = requests.NewFloat(memory)
response := ecs.CreateDescribeAvailableResourceResponse()
err = a.EcsClient.DoAction(request, response)
if err != nil {
return nil, err
}
if len(response.AvailableZones.AvailableZone) < 1 {
return nil, fmt.Errorf("resources not find")
}
for _, i := range response.AvailableZones.AvailableZone {
for _, f := range i.AvailableResources.AvailableResource {
for _, r := range f.SupportedResources.SupportedResource {
if r.StatusCategory == AvailableTypeStatus {
instanceType = append(instanceType, r.Value)
}
}
}
}
var defaultInstanceType []string
switch instanceRole {
case Master:
defaultInstanceType = []string{a.Infra.Status.MasterInstanceType}
case Node:
defaultInstanceType = []string{a.Infra.Status.NodeInstanceType}
}
instanceType = append(defaultInstanceType, instanceType...)
return
}
func (a *AliProvider) RunInstances(instanceRole string, count int) error {
var hosts *v2.Hosts
switch instanceRole {
case Master:
hosts = &a.Infra.Spec.Masters
case Node:
hosts = a.Infra.Spec.Nodes
}
instances := hosts
if instances == nil {
return errors.New("host not set")
}
systemDiskSize := instances.Disks.System
var instanceType []string
var err error
instanceType, err = a.GetAvailableResource(instanceRole, instances.CPU, instances.Memory)
if err != nil {
return err
}
tag := make(map[string]string)
tag[Product] = a.Infra.Name
tag[Role] = instanceRole
instancesTag := CreateInstanceTag(tag)
dataDisks := instances.Disks.Data
datadisk := CreateInstanceDataDisk(dataDisks, a.Infra.Spec.Instance.DataCategory)
request := ecs.CreateRunInstancesRequest()
request.Scheme = Scheme
request.ImageId = a.Infra.Spec.Instance.ImageID
request.Password = a.Infra.Spec.Auth.Passwd
request.SecurityGroupId = a.Infra.Status.SecurityGroupID
request.VSwitchId = a.Infra.Status.VSwitchID
request.SystemDiskSize = systemDiskSize
request.SystemDiskCategory = a.Infra.Spec.Instance.SystemCategory
request.DataDisk = &datadisk
request.SpotStrategy = a.Infra.Status.SpotStrategy
request.Amount = requests.NewInteger(count)
request.Tag = &instancesTag
//response, err := d.Client.RunInstances(request)
response := ecs.CreateRunInstancesResponse()
err = a.RetryEcsInstanceType(request, response, instanceType)
if err != nil {
return err
}
instancesIDs := strings.Join(response.InstanceIdSets.InstanceIdSet, ",")
switch instanceRole {
case Master:
a.Infra.Status.MasterIDs += instancesIDs
case Node:
a.Infra.Status.NodeIDs += instancesIDs
}
return nil
}
func (a *AliProvider) AuthorizeSecurityGroup(securityGroupID, portRange string) bool {
request := ecs.CreateAuthorizeSecurityGroupRequest()
request.Scheme = Scheme
request.SecurityGroupId = securityGroupID
request.IpProtocol = IPProtocol
request.PortRange = portRange
request.SourceCidrIp = SourceCidrIP
request.Policy = Policy
response := ecs.CreateAuthorizeSecurityGroupResponse()
err := a.RetryEcsRequest(request, response)
if err != nil {
logger.Error("%v", err)
return false
}
return response.BaseResponse.IsSuccess()
}
func (a *AliProvider) CreateSecurityGroup() error {
request := ecs.CreateCreateSecurityGroupRequest()
request.Scheme = Scheme
request.RegionId = a.Infra.Status.RegionID
request.VpcId = a.Infra.Status.VpcID
response := ecs.CreateCreateSecurityGroupResponse()
err := a.RetryEcsRequest(request, response)
if err != nil {
return err
}
if !a.AuthorizeSecurityGroup(response.SecurityGroupId, SSHPortRange) {
return fmt.Errorf("authorize securitygroup ssh port failed")
}
if !a.AuthorizeSecurityGroup(response.SecurityGroupId, APIServerPortRange) {
return fmt.Errorf("authorize securitygroup apiserver port failed")
}
a.Infra.Status.SecurityGroupID = response.SecurityGroupId
return nil
}
func (a *AliProvider) DeleteSecurityGroup() error {
request := ecs.CreateDeleteSecurityGroupRequest()
request.Scheme = Scheme
request.SecurityGroupId = a.Infra.Status.SecurityGroupID
response := ecs.CreateDeleteSecurityGroupResponse()
return a.RetryEcsRequest(request, response)
}
func CreateInstanceTag(tags map[string]string) (instanceTags []ecs.RunInstancesTag) {
for k, v := range tags {
instanceTags = append(instanceTags, ecs.RunInstancesTag{Key: k, Value: v})
}
return
}
func LoadConfig(config *Config) error {
config.AccessKey = os.Getenv(EnvAccessKey)
config.AccessSecret = os.Getenv(EnvAccessSecret)
config.regionID = os.Getenv(EnvRegion)
if config.regionID == "" {
config.regionID = DefaultRegionID
}
if config.AccessKey == "" || config.AccessSecret == "" || config.regionID == "" {
return fmt.Errorf("please set accessKey and accessKeySecret ENV, example: export ACCESSKEYID=xxx export ACCESSKEYSECRET=xxx , how to get AK SK: https://ram.console.aliyun.com/manage/ak")
}
return nil
}
+248
View File
@@ -0,0 +1,248 @@
// Copyright © 2021 Alibaba Group Holding Ltd.
//
// 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 aliyun
import (
"errors"
"strings"
"github.com/fanux/sealos/pkg/types/validation"
v2 "github.com/fanux/sealos/pkg/types/v1beta1"
"github.com/fanux/sealos/pkg/utils/logger"
"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
"github.com/aliyun/alibaba-cloud-sdk-go/services/vpc"
)
type ActionName string
const (
CreateVPC ActionName = "CreateVPC"
CreateVSwitch ActionName = "CreateVSwitch"
CreateSecurityGroup ActionName = "CreateSecurityGroup"
ReconcileInstance ActionName = "ReconcileInstance"
BindEIP ActionName = "BindEIP"
ReleaseEIP ActionName = "ReleaseEIP"
ClearInstances ActionName = "ClearInstances"
DeleteVSwitch ActionName = "DeleteVSwitch"
DeleteSecurityGroup ActionName = "DeleteSecurityGroup"
DeleteVPC ActionName = "DeleteVPC"
GetSystemInfo ActionName = "GetSystemInfo"
)
type AliProvider struct {
Config Config
EcsClient ecs.Client
VpcClient vpc.Client
Infra *v2.Infra
}
type Config struct {
AccessKey string
AccessSecret string
regionID string
}
type Alifunc func() error
func (a *AliProvider) ReconcileResource(resourceKey ResourceName, action Alifunc) error {
if resourceKey.Value(a.Infra.Status) != "" {
logger.Warn("create resource exists %s: %s", resourceKey, resourceKey.Value(a.Infra.Status))
return nil
}
if err := action(); err != nil {
logger.Error("reconcile resource %s failed err: %s", resourceKey, err)
return err
}
if resourceKey.Value(a.Infra.Status) != "" {
logger.Info("create resource success %s: %s", resourceKey, resourceKey.Value(a.Infra.Status))
}
return nil
}
func (a *AliProvider) DeleteResource(resourceKey ResourceName, action Alifunc) {
val := resourceKey.Value(a.Infra.Status)
if val == "" {
logger.Warn("delete resource not exists %s", resourceKey)
return
}
if err := action(); err != nil {
logger.Error("delete resource %s failed err: %s", resourceKey, err)
} else {
logger.Info("delete resource Success %s: %s", resourceKey, val)
}
}
var RecocileFuncMap = map[ActionName]func(provider *AliProvider) error{
CreateVPC: func(aliProvider *AliProvider) error {
return aliProvider.ReconcileResource(VpcID, aliProvider.CreateVPC)
},
CreateVSwitch: func(aliProvider *AliProvider) error {
return aliProvider.ReconcileResource(VSwitchID, aliProvider.CreateVSwitch)
},
CreateSecurityGroup: func(aliProvider *AliProvider) error {
return aliProvider.ReconcileResource(SecurityGroupID, aliProvider.CreateSecurityGroup)
},
ReconcileInstance: func(aliProvider *AliProvider) error {
err := aliProvider.ReconcileInstances(Master)
if err != nil {
return err
}
err = aliProvider.ReconcileInstances(Node)
if err != nil {
return err
}
return nil
},
GetSystemInfo: func(aliProvider *AliProvider) error {
return aliProvider.ReconcileResource(SystemInfo, aliProvider.SystemInfo)
},
BindEIP: func(aliProvider *AliProvider) error {
return aliProvider.ReconcileResource(EipID, aliProvider.BindEipForMaster0)
},
}
var DeleteFuncMap = map[ActionName]func(provider *AliProvider){
ReleaseEIP: func(aliProvider *AliProvider) {
aliProvider.DeleteResource(EipID, aliProvider.ReleaseEipAddress)
},
ClearInstances: func(aliProvider *AliProvider) {
var instanceIDs []string
roles := []string{Master, Node}
for _, role := range roles {
instances, err := aliProvider.GetInstancesInfo(role, JustGetInstanceInfo)
if err != nil {
logger.Error("get %s instanceinfo failed %v", role, err)
}
for _, instance := range instances {
instanceIDs = append(instanceIDs, instance.InstanceID)
}
}
if len(instanceIDs) != 0 {
aliProvider.Infra.Status.ShouldBeDeleteInstancesIDs = strings.Join(instanceIDs, ",")
}
aliProvider.DeleteResource(ShouldBeDeleteInstancesIDs, aliProvider.DeleteInstances)
},
DeleteVSwitch: func(aliProvider *AliProvider) {
aliProvider.DeleteResource(VSwitchID, aliProvider.DeleteVSwitch)
},
DeleteSecurityGroup: func(aliProvider *AliProvider) {
aliProvider.DeleteResource(SecurityGroupID, aliProvider.DeleteSecurityGroup)
},
DeleteVPC: func(aliProvider *AliProvider) {
aliProvider.DeleteResource(VpcID, aliProvider.DeleteVPC)
},
}
func (a *AliProvider) NewClient() error {
ecsClient, err := ecs.NewClientWithAccessKey(a.Config.regionID, a.Config.AccessKey, a.Config.AccessSecret)
if err != nil {
return err
}
vpcClient, err := vpc.NewClientWithAccessKey(a.Config.regionID, a.Config.AccessKey, a.Config.AccessSecret)
if err != nil {
return err
}
a.EcsClient = *ecsClient
a.VpcClient = *vpcClient
a.Infra.Status.RegionID = a.Config.regionID
return nil
}
func (a *AliProvider) ClearCluster() {
todolist := []ActionName{
ReleaseEIP,
ClearInstances,
DeleteVSwitch,
DeleteSecurityGroup,
DeleteVPC,
}
for _, name := range todolist {
DeleteFuncMap[name](a)
}
}
func (a *AliProvider) Reconcile() error {
if a.Infra.Annotations == nil {
a.Infra.Annotations = make(map[string]string)
}
if a.Infra.DeletionTimestamp != nil {
logger.Info("DeletionTimestamp not nil Clear Infra")
a.ClearCluster()
return nil
}
todolist := []ActionName{
CreateVPC,
GetSystemInfo,
CreateVSwitch,
CreateSecurityGroup,
ReconcileInstance,
BindEIP,
}
for _, actionname := range todolist {
err := RecocileFuncMap[actionname](a)
if err != nil {
return err
}
}
return nil
}
func (a *AliProvider) Apply() error {
if err := v2.Default(a.Infra, defaultInfra); err != nil {
return err
}
if err := validation.ValidateInfra(a.Infra); len(err) != 0 {
return err.ToAggregate()
}
return a.Reconcile()
}
func defaultInfra(infra *v2.Infra) error {
//https://help.aliyun.com/document_detail/63440.htm?spm=a2c4g.11186623.0.0.f5952752gkxpB7#t9856.html
if infra.Spec.Instance.IsSeize {
infra.Status.SpotStrategy = "SpotAsPriceGo"
} else {
infra.Status.SpotStrategy = "NoSpot"
}
if infra.Spec.Platform == v2.ARM64 {
switch infra.Status.RegionID {
case "cn-shanghai":
infra.Status.ZoneID = "cn-shanghai-l"
case "cn-beijing":
infra.Status.ZoneID = "cn-beijing-k"
case "cn-hangzhou":
infra.Status.ZoneID = "cn-hangzhou-i"
default:
return errors.New("not available ZoneID for arm, support RegionID[cn-shanghai,cn-beijing,cn-hangzhou]")
}
infra.Spec.Instance.ImageID = defaultImageArmID
} else {
infra.Spec.Instance.ImageID = defaultImageAmdID
}
//"cloud_efficiency", "cloud_essd", "cloud_ssd"
if infra.Spec.Instance.SystemCategory == "" {
infra.Spec.Instance.SystemCategory = "cloud_efficiency"
}
if infra.Spec.Instance.DataCategory == "" {
infra.Spec.Instance.DataCategory = "cloud_efficiency"
}
return nil
}
+175
View File
@@ -0,0 +1,175 @@
// Copyright © 2021 Alibaba Group Holding Ltd.
//
// 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 aliyun
import (
"errors"
"github.com/fanux/sealos/pkg/utils/logger"
"github.com/fanux/sealos/pkg/utils"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses"
"github.com/aliyun/alibaba-cloud-sdk-go/services/vpc"
)
type VpcManager struct {
Config Config
Client *vpc.Client
}
func (a *AliProvider) RetryVpcRequest(request requests.AcsRequest, response responses.AcsResponse) error {
return utils.Retry(TryTimes, TrySleepTime, func() error {
err := a.VpcClient.DoAction(request, response)
if err != nil {
return err
}
return nil
})
}
func (a *AliProvider) CreateVPC() error {
request := vpc.CreateCreateVpcRequest()
request.Scheme = Scheme
request.RegionId = a.Infra.Status.RegionID
//response, err := d.Client.CreateVpc(request)
response := vpc.CreateCreateVpcResponse()
err := a.RetryVpcRequest(request, response)
if err != nil {
return err
}
a.Infra.Status.VpcID = response.VpcId
return nil
}
func (a *AliProvider) DeleteVPC() error {
request := vpc.CreateDeleteVpcRequest()
request.Scheme = Scheme
request.VpcId = a.Infra.Status.VpcID
//response, err := d.Client.DeleteVpc(request)
response := vpc.CreateDeleteVpcResponse()
return a.RetryVpcRequest(request, response)
}
func (a *AliProvider) CreateVSwitch() error {
request := vpc.CreateCreateVSwitchRequest()
request.Scheme = Scheme
request.ZoneId = a.Infra.Status.ZoneID
request.CidrBlock = CidrBlock
request.VpcId = a.Infra.Status.VpcID
request.RegionId = a.Infra.Status.RegionID
//response, err := d.Client.CreateVSwitch(request)
response := vpc.CreateCreateVSwitchResponse()
err := a.RetryVpcRequest(request, response)
if err != nil {
return err
}
a.Infra.Status.VSwitchID = response.VSwitchId
return nil
}
func (a *AliProvider) DeleteVSwitch() error {
request := vpc.CreateDeleteVSwitchRequest()
request.Scheme = Scheme
request.VSwitchId = a.Infra.Status.VSwitchID
response := vpc.CreateDeleteVSwitchResponse()
return a.RetryVpcRequest(request, response)
}
func (a *AliProvider) SystemInfo() error {
availableInstance, err := a.GetAvailableResourcesForSystem()
if err != nil {
return err
}
a.Infra.Spec.Instance.Type = availableInstance.InstanceType
a.Infra.Status.MasterInstanceType = availableInstance.InstanceType
a.Infra.Status.NodeInstanceType = availableInstance.InstanceType
a.Infra.Status.ZoneID = availableInstance.ZoneID
logger.Info("fetch resource success %s: %s", "ZoneID", a.Infra.Status.ZoneID)
logger.Info("fetch resource success %s: %s", "InstanceType", a.Infra.Spec.Instance.Type)
return nil
}
func (a *AliProvider) BindEipForMaster0() error {
instances, err := a.GetInstancesInfo(Master, JustGetInstanceInfo)
if err != nil {
return err
}
if len(instances) == 0 {
return errors.New("can not find master0 ")
}
master0 := instances[0]
eIP, eIPID, err := a.AllocateEipAddress()
if err != nil {
return err
}
err = a.AssociateEipAddress(master0.InstanceID, eIPID)
if err != nil {
return err
}
a.Infra.Status.EIP = eIP
a.Infra.Status.EIPID = eIPID
a.Infra.Status.Master0ID = master0.InstanceID
a.Infra.Status.Master0InternalIP = master0.PrimaryIPAddress
return nil
}
func (a *AliProvider) AllocateEipAddress() (eIP, eIPID string, err error) {
request := vpc.CreateAllocateEipAddressRequest()
request.Scheme = Scheme
request.Bandwidth = Bandwidth
request.InternetChargeType = InternetChargeType
response := vpc.CreateAllocateEipAddressResponse()
err = a.RetryVpcRequest(request, response)
if err != nil {
return "", "", err
}
return response.EipAddress, response.AllocationId, nil
}
func (a *AliProvider) AssociateEipAddress(instanceID, eipID string) error {
request := vpc.CreateAssociateEipAddressRequest()
request.Scheme = Scheme
request.InstanceId = instanceID
request.AllocationId = eipID
response := vpc.CreateAssociateEipAddressResponse()
return a.RetryVpcRequest(request, response)
}
func (a *AliProvider) UnassociateEipAddress() error {
request := vpc.CreateUnassociateEipAddressRequest()
request.Scheme = Scheme
request.AllocationId = a.Infra.Status.EIPID
request.Force = requests.NewBoolean(true)
response := vpc.CreateUnassociateEipAddressResponse()
return a.RetryVpcRequest(request, response)
}
func (a *AliProvider) ReleaseEipAddress() error {
err := a.UnassociateEipAddress()
if err != nil {
return err
}
request := vpc.CreateReleaseEipAddressRequest()
request.Scheme = Scheme
request.AllocationId = a.Infra.Status.EIPID
response := vpc.CreateReleaseEipAddressResponse()
return a.RetryVpcRequest(request, response)
}
+53
View File
@@ -0,0 +1,53 @@
// Copyright © 2021 Alibaba Group Holding Ltd.
//
// 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 infra
import (
"fmt"
"github.com/fanux/sealos/pkg/infra/aliyun"
v2 "github.com/fanux/sealos/pkg/types/v1beta1"
)
type Interface interface {
// Apply apply iaas resources and save metadata info like vpc instance id to cluster status
// https://github.com/fanux/sealgate/tree/master/cloud
Apply() error
}
func newAliProvider(infra *v2.Infra) (Interface, error) {
config := new(aliyun.Config)
err := aliyun.LoadConfig(config)
if err != nil {
return nil, err
}
aliProvider := new(aliyun.AliProvider)
aliProvider.Config = *config
aliProvider.Infra = infra
err = aliProvider.NewClient()
if err != nil {
return nil, err
}
return aliProvider, nil
}
func NewDefaultProvider(infra *v2.Infra) (Interface, error) {
switch infra.Spec.Provider {
case v2.AliyunProvider:
return newAliProvider(infra)
default:
return nil, fmt.Errorf("the provider is invalid, please set the provider correctly")
}
}
+123
View File
@@ -0,0 +1,123 @@
// Copyright © 2021 Alibaba Group Holding Ltd.
//
// 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 infra
import (
"fmt"
"os"
"testing"
"time"
"sigs.k8s.io/yaml"
v2 "github.com/fanux/sealos/pkg/types/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestApply(t *testing.T) {
//setup infra
password := os.Getenv("SealosPassword")
infra := v2.Infra{
TypeMeta: metav1.TypeMeta{
Kind: "Infra",
APIVersion: v2.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-infra",
},
Spec: v2.InfraSpec{
Instance: v2.Instance{
IsSeize: true,
Type: "ecs.c6r.large",
},
Platform: v2.ARM64,
Masters: v2.Hosts{
Count: 1,
CPU: 2,
Memory: 4,
Disks: v2.Disks{
System: "100",
Data: []string{"100"},
},
},
Nodes: &v2.Hosts{
Count: 1,
CPU: 2,
Memory: 4,
Disks: v2.Disks{
System: "100",
Data: []string{"100"},
},
},
Provider: v2.AliyunProvider,
Auth: v2.Auth{
Passwd: password,
},
},
}
aliProvider, err := NewDefaultProvider(&infra)
if err != nil {
fmt.Printf("%v", err)
} else {
fmt.Printf("%v", aliProvider.Apply())
}
t.Run("modify instance type", func(t *testing.T) {
infra.Spec.Masters.CPU = 4
infra.Spec.Masters.Memory = 8
infra.Spec.Nodes.CPU = 4
infra.Spec.Nodes.Memory = 8
fmt.Printf("%v", aliProvider.Apply())
})
t.Run("add instance count", func(t *testing.T) {
infra.Spec.Masters.Count = 5
infra.Spec.Nodes.Count = 5
fmt.Printf("%v", aliProvider.Apply())
fmt.Printf("%v \n", infra.Spec.Masters)
fmt.Printf("%v \n", infra.Spec.Nodes)
})
t.Run("reduce instance count", func(t *testing.T) {
infra.Spec.Masters.Count = 1
infra.Spec.Nodes.Count = 1
fmt.Printf("%v", aliProvider.Apply())
})
t.Run("modify instance type & count both", func(t *testing.T) {
infra.Spec.Masters.CPU = 8
infra.Spec.Masters.Memory = 16
infra.Spec.Nodes.CPU = 8
infra.Spec.Nodes.Memory = 16
infra.Spec.Masters.Count = 5
infra.Spec.Nodes.Count = 5
fmt.Printf("%v", aliProvider.Apply())
})
// todo
t.Run("modify instance system disk", func(t *testing.T) {
})
j, _ := yaml.Marshal(&infra)
t.Log("output yaml:", string(j))
//teardown
time.Sleep(20 * time.Second)
now := metav1.Now()
infra.ObjectMeta.DeletionTimestamp = &now
t.Log(fmt.Sprintf("%v", aliProvider.Apply()))
}
+1 -1
View File
@@ -18,7 +18,7 @@ import (
"fmt"
"os"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/cni"
+1 -1
View File
@@ -20,7 +20,7 @@ import (
"strings"
"sync"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/utils/ssh"
-202
View File
@@ -1,202 +0,0 @@
// Copyright © 2021 sealos.
//
// 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 install
import (
"fmt"
"os"
"strings"
v1 "github.com/fanux/sealos/pkg/types/v1"
"github.com/fanux/sealos/pkg/utils/logger"
"github.com/fanux/sealgate/cloud"
extver "github.com/linuxsuren/cobra-extension/version"
)
//VersionURL is base64 encode k8s version and offline package url
var (
VersionURL string
URLMap map[string]string
DefaultURL = "https://sealyun.oss-cn-beijing.aliyuncs.com/37374d999dbadb788ef0461844a70151-1.16.0/kube1.16.0.tar.gz"
)
//Flags is command line paras
type Flags struct {
Master int
MasterType string
Node int
NodeType string
Version string
Flavor string
Passwd string
Zone string
Interaction bool
Image string
}
// Cluster is cluster metadata
type Cluster struct {
cloud.Config
Flags
Name string
Masters []cloud.VM
Nodes []cloud.VM
VPCID string
SwitchID string
SecuretyGroupID string
}
var C Cluster
var ClusterDir = v1.DefaultConfigPath + "/clusters/"
// 2019.11.28 今天刚修完陪产假,在新装修的公寓中写代码,刚配的眼镜感觉带着有点不舒服,看屏幕不是很清楚
/*
配眼镜的人也不是很专业,二宝还是个女儿,非常可爱而且非常乖,不像大宝那么吵。 一直很想写日记但是不知道往哪里写合适
既然github要把代码存两千年那为啥不写到代码里,如此这便成为我第一篇代码日记。
碳纤维地暖开了半天还是冰凉的,感觉是被忽悠了。
一写代码就精神万分,一搞管理上的杂事就效率很低,所以做技术还是要专注些。
*/
func CloudInstall(c *Cluster) {
URLMap = make(map[string]string)
URLMap["v1.16.0"] = DefaultURL
config := c.Config
p := cloud.NewProvider(config)
_ = v1.Dump(fmt.Sprintf("%s%s.yaml", ClusterDir, c.Name), c)
//TODO concurrence create master and nodes vms, should not create two vpcs
/*
var wg sync.WaitGroup
wg.Add(2)
go func() {
// create masters vms
res, err := p.Create(newRequest(c, "master", true, c.Master))
if err != nil {
logger.Error("init cluster failed: %s", err)
return
}
c.Masters = res.VMs
c.VPCID = res.VPCID
c.SwitchID = res.SwitchID
c.SecuretyGroupID = res.SecuretyGroupID
wg.Done()
}()
go func() {
// create nodes vms
res, err := p.Create(newRequest(c, "node", false, c.Node))
if err != nil {
logger.Error("init cluster failed: %s", err)
return
}
c.Nodes = res.VMs
wg.Done()
}()
wg.Wait()
*/
// create masters vms
res, err := p.Create(newRequest(c, "master", true, c.Master))
if err != nil {
logger.Error("init cluster failed: %s", err)
return
}
c.Masters = res.VMs
c.VPCID = res.VPCID
c.SwitchID = res.SwitchID
c.SecuretyGroupID = res.SecuretyGroupID
// create nodes vms
res, err = p.Create(newRequest(c, "node", false, c.Node))
if err != nil {
logger.Error("init cluster failed: %s", err)
return
}
c.Nodes = res.VMs
//TODO wget package on master0 and scp to other nodes
logger.Info("wait few minute for download offline package on master0...")
cmd := newWgetCommand(c)
CmdWorkSpace(c.Masters[0].FIP, cmd, "/root")
// exec sealos init on master0
cmd = newCommand(c)
CmdWorkSpace(c.Masters[0].FIP, cmd, "/root")
}
func getURL(version string) string {
url, ok := URLMap[version]
if !ok {
logger.Error("version offline package not found: %s", version)
os.Exit(1)
return DefaultURL
}
return url
}
func getLocalURL(version string) string {
return fmt.Sprintf("/root/kube%s.tar.gz", version[1:])
}
func newCommand(c *Cluster) string {
//TODO should download it on master0 and copy to other nodes
version := extver.GetVersion()
if strings.HasPrefix(version, "v") {
version = strings.TrimPrefix(version, "v")
}
releaseURL := fmt.Sprintf("https://github.com/fanux/sealos/releases/download/v%s/sealos_%s_linux_amd64.tar.gz",
version, version)
cmd := fmt.Sprintf("wget %s -O -| tar -xz && chmod +x sealos", releaseURL)
cmd += fmt.Sprintf(" && ./sealos init --passwd %s --pkg-url %s --version %s", c.Passwd, getLocalURL(c.Version), c.Version)
for _, master := range c.Masters {
cmd += fmt.Sprintf(" --master %s", master.IP)
}
for _, node := range c.Nodes {
cmd += fmt.Sprintf(" --node %s", node.IP)
}
return cmd
}
func newWgetCommand(c *Cluster) string {
cmd := fmt.Sprintf("cd /root && wget %s", getURL(c.Version))
return cmd
}
func newRequest(c *Cluster, namePrefix string, fip bool, num int) cloud.Request {
r := cloud.Request{
Num: num,
Image: c.Flags.Image,
NamePrefix: namePrefix,
FIP: fip,
Flavor: c.Flags.Flavor,
Passwd: c.Flags.Passwd,
ZoneID: c.Flags.Zone,
VPCID: c.VPCID,
SwitchID: c.SwitchID,
SecuretyGroupID: c.SecuretyGroupID,
}
return r
}
// CmdWorkSpace exec cmd on specified workdir.
func CmdWorkSpace(node, cmd, workdir string) {
command := fmt.Sprintf("cd %s && %s", workdir, cmd)
_ = v1.SSHConfig.CmdAsync(node, command)
}
+1 -1
View File
@@ -22,7 +22,7 @@ import (
"sync"
"time"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/utils/ssh"
+1 -1
View File
@@ -23,7 +23,7 @@ import (
"sync"
"time"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/utils/ssh"
+8 -1
View File
@@ -15,11 +15,12 @@
package install
import (
"fmt"
"os"
"strings"
"sync"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/kubernetes/nodeclient"
@@ -154,3 +155,9 @@ func (e *ExecFlag) execByNodeIP() {
}
wg.Wait()
}
// CmdWorkSpace exec cmd on specified workdir.
func CmdWorkSpace(node, cmd, workdir string) {
command := fmt.Sprintf("cd %s && %s", workdir, cmd)
_ = v1.SSHConfig.CmdAsync(node, command)
}
+1 -1
View File
@@ -15,7 +15,7 @@
package install
import (
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/utils/logger"
)
+1 -1
View File
@@ -20,7 +20,7 @@ import (
"strings"
"text/template"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/utils/logger"
+1 -1
View File
@@ -18,7 +18,7 @@ import (
"reflect"
"testing"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/utils/ssh"
+1 -1
View File
@@ -24,7 +24,7 @@ import (
"path/filepath"
"strings"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/utils/ssh"
+1 -1
View File
@@ -19,7 +19,7 @@ import (
"strings"
"sync"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/utils/ssh"
+1 -1
View File
@@ -18,7 +18,7 @@ import (
"fmt"
"strings"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/cni"
+1 -1
View File
@@ -18,7 +18,7 @@ import (
"fmt"
"path"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/utils/ssh"
)
+1 -1
View File
@@ -19,7 +19,7 @@ import (
"os"
"time"
v1 "github.com/fanux/sealos/pkg/types/v1"
v1 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
nodeclient2 "github.com/fanux/sealos/pkg/kubernetes/nodeclient"
-37
View File
@@ -1,37 +0,0 @@
/*
Copyright 2014 The Kubernetes 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 version
// Info contains versioning information.
// TODO: Add []string of api versions supported? It's still unclear
// how we'll want to distribute that information.
type Info struct {
Major string `json:"major"`
Minor string `json:"minor"`
GitVersion string `json:"gitVersion"`
GitCommit string `json:"gitCommit"`
GitTreeState string `json:"gitTreeState"`
BuildDate string `json:"buildDate"`
GoVersion string `json:"goVersion"`
Compiler string `json:"compiler"`
Platform string `json:"platform"`
}
// String returns info as a human-friendly version string.
func (info Info) String() string {
return info.GitVersion
}
+1 -1
View File
@@ -228,7 +228,7 @@ func validateKubeConfig(outDir, filename string, config *clientcmdapi.Config) er
}
currentCluster := currentCtx.Cluster
if currentConfig.Clusters[currentCluster] == nil {
return errors.Errorf("failed to find the given CurrentContext Cluster in Clusters of the kubeconfig file %s", kubeConfigFilePath)
return errors.Errorf("failed to find the given CurrentContext Infra in Clusters of the kubeconfig file %s", kubeConfigFilePath)
}
// Make sure the compared CAs are whitespace-trimmed. The function clientcmd.LoadFromFile() just decodes
+1 -1
View File
@@ -16,7 +16,7 @@ package kubeconfig
import clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
// GetClusterFromKubeConfig returns the default Cluster of the specified KubeConfig
// GetClusterFromKubeConfig returns the default Infra of the specified KubeConfig
func GetClusterFromKubeConfig(config *clientcmdapi.Config) *clientcmdapi.Cluster {
// If there is an unnamed cluster object, use it
if config.Clusters[""] != nil {
+1 -1
View File
@@ -19,7 +19,7 @@ import (
"fmt"
"time"
v12 "github.com/fanux/sealos/pkg/types/v1"
v12 "github.com/fanux/sealos/pkg/types/v1alpha1"
"github.com/fanux/sealos/pkg/utils"
v1 "k8s.io/api/core/v1"
@@ -12,19 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package v1
package v1alpha1
import (
"fmt"
"io/ioutil"
"os"
"sigs.k8s.io/yaml"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/cni"
"github.com/fanux/sealos/pkg/utils/logger"
v2 "gopkg.in/yaml.v2"
)
const (
@@ -44,40 +44,40 @@ type Metadata struct {
// SealConfig for ~/.sealos/config.yaml
type SealConfig struct {
Masters []string
Nodes []string
Masters []string `json:"masters"`
Nodes []string `json:"nodes"`
//config from kubeadm.cfg. ex. cluster.local
DNSDomain string
APIServerCertSANs []string
DNSDomain string `json:"dnsdomain"`
APIServerCertSANs []string `json:"apiservercertsans"`
//SSHConfig
User string
Passwd string
PrivateKey string
PkPassword string
User string `json:"user"`
Passwd string `json:"passwd"`
PrivateKey string `json:"privatekey"`
PkPassword string `json:"pkpassword"`
//ApiServer ex. apiserver.cluster.local
APIServerDomain string
Network string
VIP string
PkgURL string
Version string
Repo string
PodCIDR string
SvcCIDR string
APIServerDomain string `json:"apiserverdomain"`
Network string `json:"network"`
VIP string `json:"vip"`
PkgURL string `json:"pkgurl"`
Version string `json:"version"`
Repo string `json:"repo"`
PodCIDR string `json:"podcidr"`
SvcCIDR string `json:"svccidr"`
//certs location
CertPath string
CertEtcdPath string
CertPath string `json:"certpath"`
CertEtcdPath string `json:"certetcdpath"`
//lvscare images
LvscareName string
LvscareTag string
AliOss
LvscareName string `json:"lvscarename"`
LvscareTag string `json:"lvscaretag"`
AliOss `json:"alioss"`
}
type AliOss struct {
OssEndpoint string
AccessKeyID string
AccessKeySecrets string
BucketName string
ObjectPath string
OssEndpoint string `json:"ossendpoint"`
AccessKeyID string `json:"accesskeyid"`
AccessKeySecrets string `json:"accesskeysecrets"`
BucketName string `json:"bucketname"`
ObjectPath string `json:"objectpath"`
}
//Dump is
@@ -115,7 +115,7 @@ func (c *SealConfig) Dump(path string) {
c.AliOss.OssEndpoint = OssEndpoint
c.AliOss.BucketName = BucketName
c.AliOss.ObjectPath = ObjectPath
y, err := v2.Marshal(c)
y, err := yaml.Marshal(c)
if err != nil {
logger.Error("dump config file failed: %s", err)
}
@@ -131,7 +131,7 @@ func (c *SealConfig) Dump(path string) {
}
func Dump(path string, content interface{}) error {
y, err := v2.Marshal(content)
y, err := yaml.Marshal(content)
if err != nil {
logger.Error("dump config file failed: %s", err)
return err
@@ -157,7 +157,7 @@ func (c *SealConfig) Load(path string) (err error) {
return fmt.Errorf("read config file %s failed %w", path, err)
}
err = v2.Unmarshal(y, c)
err = yaml.Unmarshal(y, c)
if err != nil {
return fmt.Errorf("unmarshal config file failed: %w", err)
}
@@ -203,7 +203,7 @@ func Load(path string, content interface{}) error {
os.Exit(0)
}
err = v2.Unmarshal(y, content)
err = yaml.Unmarshal(y, content)
if err != nil {
logger.Error("unmarshal config file failed: %s", err)
}
@@ -231,7 +231,7 @@ func (c *SealConfig) ShowDefaultConfig() {
c.LvscareName = "fanux/lvscare"
c.LvscareTag = "latest"
y, err := v2.Marshal(c)
y, err := yaml.Marshal(c)
if err != nil {
logger.Error("marshal config file failed: %s", err)
}
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package v1
package v1alpha1
import (
"strconv"
+67
View File
@@ -0,0 +1,67 @@
/*
Copyright 2021 cuisongliu@qq.com.
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 (
"math/rand"
"time"
)
func Default(infra *Infra, fn func(infra *Infra) error) error {
if string(infra.Spec.Platform) == "" {
infra.Spec.Platform = AMD64
}
if infra.Spec.Auth.User == "" {
infra.Spec.Auth.User = "root"
}
if infra.Spec.Masters.CPU <= 0 {
infra.Spec.Masters.CPU = 2
}
if infra.Spec.Masters.Memory <= 0 {
infra.Spec.Masters.Memory = 4
}
if len(infra.Spec.Masters.Disks.System) == 0 {
infra.Spec.Masters.Disks.System = "40"
}
if infra.Spec.Auth.Passwd == "" {
infra.Spec.Auth.Passwd = createPassword()
}
return fn(infra)
}
const (
digits = "0123456789"
specials = "~=+%^*/()[]{}/!@#$?|"
letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
passwordLength = 16
)
func createPassword() string {
rand.Seed(time.Now().UnixNano())
all := digits + specials + letter
length := passwordLength
buf := make([]byte, length)
buf[0] = digits[rand.Intn(len(digits))]
buf[1] = specials[rand.Intn(len(specials))]
for i := 2; i < length; i++ {
buf[i] = all[rand.Intn(len(all))]
}
rand.Shuffle(len(buf), func(i, j int) {
buf[i], buf[j] = buf[j], buf[i]
})
return string(buf)
}
+21
View File
@@ -0,0 +1,21 @@
// Copyright © 2021 sealos.
//
// 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
// +k8s:protobuf-gen=package
// +k8s:openapi-gen=false
// +k8s:defaulter-gen=TypeMeta
// +groupName=apps.sealyun.com
package v1beta1
+25
View File
@@ -0,0 +1,25 @@
// Copyright © 2021 sealos.
//
// 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/schema"
)
// GroupName is the group name use in this package
const GroupName = "apps.sealyun.com"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
+123
View File
@@ -0,0 +1,123 @@
// Copyright © 2021 sealos.
//
// 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 (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
type Provider string
const (
AliyunProvider Provider = "AliyunProvider"
)
type Platform string
const (
AMD64 Platform = "amd64"
ARM64 Platform = "arm64"
)
type Auth struct {
User string `json:"user,omitempty"`
Passwd string `json:"passwd,omitempty"`
}
type Hosts struct {
CPU int `json:"cpu,omitempty"`
Memory float64 `json:"memory,omitempty"`
Count int `json:"count,omitempty"`
Disks Disks `json:"disks"`
}
type Disks struct {
System string `json:"system,omitempty"`
Data []string `json:"data,omitempty"`
}
type Instance struct {
SystemCategory string `json:"systemCategory,omitempty"`
DataCategory string `json:"dataCategory,omitempty"`
Type string `json:"type,omitempty"`
IsSeize bool `json:"isSeize,omitempty"`
ImageID string `json:"imageID,omitempty"`
}
// InfraSpec defines the desired state of Infra
type InfraSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of Infra
// Important: Run "make" to regenerate code after modifying this file
Instance Instance `json:"instance"`
// Foo is an example field of Infra. Edit types.go to remove/update
Provider Provider `json:"provider,omitempty"`
Platform Platform `json:"platform,omitempty"`
Auth Auth `json:"auth,omitempty"`
Masters Hosts `json:"masters,omitempty"`
Nodes *Hosts `json:"nodes,omitempty"`
}
// InfraStatus defines the observed state of Infra
type InfraStatus struct {
ZoneID string `json:"zoneID,omitempty"`
RegionID string `json:"regionID,omitempty"`
VpcID string `json:"vpcID,omitempty"`
VSwitchID string `json:"vSwitchID,omitempty"`
SecurityGroupID string `json:"securityGroupID,omitempty"`
Master0ID string `json:"master0ID,omitempty"`
Master0InternalIP string `json:"master0InternalIP,omitempty"`
EIP string `json:"eip,omitempty"`
EIPID string `json:"eipID,omitempty"`
MasterIDs string `json:"masterIDs,omitempty"`
NodeIDs string `json:"nodeIDs,omitempty"`
Masters []string `json:"masters,omitempty"`
Nodes []string `json:"nodes,omitempty"`
MasterInstanceType string `json:"masterInstanceType,omitempty"`
NodeInstanceType string `json:"nodeInstanceType,omitempty"`
SpotStrategy string `json:"spotStrategy,omitempty"`
ShouldBeDeleteInstancesIDs string `json:"shouldBeDeleteInstancesIDs,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Infra is the Schema for the Infras API
type Infra struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec InfraSpec `json:"spec,omitempty"`
Status InfraStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// InfraList contains a list of Infra
type InfraList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Infra `json:"items"`
}
+204
View File
@@ -0,0 +1,204 @@
// +build !ignore_autogenerated
// Copyright © 2021 sealos.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1beta1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Auth) DeepCopyInto(out *Auth) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Auth.
func (in *Auth) DeepCopy() *Auth {
if in == nil {
return nil
}
out := new(Auth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Disks) DeepCopyInto(out *Disks) {
*out = *in
if in.Data != nil {
in, out := &in.Data, &out.Data
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Disks.
func (in *Disks) DeepCopy() *Disks {
if in == nil {
return nil
}
out := new(Disks)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Hosts) DeepCopyInto(out *Hosts) {
*out = *in
in.Disks.DeepCopyInto(&out.Disks)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Hosts.
func (in *Hosts) DeepCopy() *Hosts {
if in == nil {
return nil
}
out := new(Hosts)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Infra) DeepCopyInto(out *Infra) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Infra.
func (in *Infra) DeepCopy() *Infra {
if in == nil {
return nil
}
out := new(Infra)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Infra) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *InfraList) DeepCopyInto(out *InfraList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Infra, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfraList.
func (in *InfraList) DeepCopy() *InfraList {
if in == nil {
return nil
}
out := new(InfraList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *InfraList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *InfraSpec) DeepCopyInto(out *InfraSpec) {
*out = *in
out.Instance = in.Instance
out.Auth = in.Auth
in.Masters.DeepCopyInto(&out.Masters)
if in.Nodes != nil {
in, out := &in.Nodes, &out.Nodes
*out = new(Hosts)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfraSpec.
func (in *InfraSpec) DeepCopy() *InfraSpec {
if in == nil {
return nil
}
out := new(InfraSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *InfraStatus) DeepCopyInto(out *InfraStatus) {
*out = *in
if in.Masters != nil {
in, out := &in.Masters, &out.Masters
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Nodes != nil {
in, out := &in.Nodes, &out.Nodes
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfraStatus.
func (in *InfraStatus) DeepCopy() *InfraStatus {
if in == nil {
return nil
}
out := new(InfraStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Instance) DeepCopyInto(out *Instance) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Instance.
func (in *Instance) DeepCopy() *Instance {
if in == nil {
return nil
}
out := new(Instance)
in.DeepCopyInto(out)
return out
}
+94
View File
@@ -0,0 +1,94 @@
// Copyright © 2021 sealos.
//
// 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 validation
import (
"github.com/fanux/sealos/pkg/types/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
)
// ValidateInfraName validates that the given name can be used as a infra name.
var ValidateInfraName = apimachineryvalidation.NameIsDNSSubdomain
func validateHosts(hosts *v1beta1.Hosts, fldPath *field.Path) field.ErrorList {
allErrors := field.ErrorList{}
if hosts.Count == 0 {
allErrors = append(allErrors, field.Invalid(fldPath.Key("count"), hosts.Count,
"hosts count not set"))
}
if hosts.CPU == 0 {
allErrors = append(allErrors, field.Invalid(fldPath.Key("cpu"), hosts.Count,
"hosts cpu not set"))
}
if hosts.Memory == 0 {
allErrors = append(allErrors, field.Invalid(fldPath.Key("memory"), hosts.Count,
"hosts memory not set"))
}
if len(hosts.Disks.System) == 0 {
allErrors = append(allErrors, field.Invalid(fldPath.Child("disks").Key("system"), hosts.Count,
"hosts system disk not set"))
}
return allErrors
}
func ValidateInfra(infra *v1beta1.Infra) field.ErrorList {
allErrors := apimachineryvalidation.ValidateObjectMeta(&infra.ObjectMeta, false, ValidateInfraName, field.NewPath("metadata"))
allErrors = append(allErrors, validateInfraSpec(&infra.Spec, field.NewPath("spec"))...)
return allErrors
}
func validateInfraSpec(spec *v1beta1.InfraSpec, fldPath *field.Path) field.ErrorList {
allErrors := field.ErrorList{}
allErrors = append(allErrors, validateHosts(&spec.Masters, fldPath.Child("masters"))...)
if spec.Nodes != nil {
allErrors = append(allErrors, validateHosts(spec.Nodes, fldPath.Child("nodes"))...)
}
switch spec.Platform {
case v1beta1.ARM64:
case v1beta1.AMD64:
default:
allErrors = append(allErrors, field.Invalid(fldPath.Key("platform"), spec.Platform,
"spec platform not support"))
}
return allErrors
}
func ValidateInfraUpdate(infra, oldInfra *v1beta1.Infra) field.ErrorList {
allErrs := apimachineryvalidation.ValidateObjectMetaUpdate(&infra.ObjectMeta, &oldInfra.ObjectMeta, field.NewPath("metadata"))
newInfraClone := infra.DeepCopy()
if newInfraClone.Spec.Provider != oldInfra.Spec.Provider {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Key("provider"), "fields can't modify are forbidden"))
}
if newInfraClone.Spec.Platform != oldInfra.Spec.Platform {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Key("platform"), "fields can't modify are forbidden"))
}
if !apiequality.Semantic.DeepEqual(newInfraClone.Spec.Auth, oldInfra.Spec.Auth) {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Key("auth"), "fields can't modify are forbidden"))
}
if !apiequality.Semantic.DeepEqual(newInfraClone.Spec.Masters.Disks, oldInfra.Spec.Masters.Disks) {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("masters").Key("disks"), "fields can't modify are forbidden"))
}
if newInfraClone.Spec.Nodes != nil && oldInfra.Spec.Nodes != nil {
if !apiequality.Semantic.DeepEqual(newInfraClone.Spec.Nodes.Disks, oldInfra.Spec.Nodes.Disks) {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("nodes").Key("disks"), "fields can't modify are forbidden"))
}
}
return allErrs
}
+6 -10
View File
@@ -17,13 +17,11 @@ package utils
import (
"fmt"
"os"
"github.com/fanux/sealos/pkg/utils/logger"
)
const md5sumCmd = "md5sum %s | cut -d\" \" -f1"
func FromLocal(localPath string) string {
func Md5File(localPath string) string {
cmd := fmt.Sprintf(md5sumCmd, localPath)
return BashEval(cmd)
}
@@ -37,13 +35,11 @@ func UserHomeDir() string {
return home
}
func FileExist(filename string) bool {
_, err := os.Stat(filename)
if !os.IsNotExist(err) {
if err == nil {
return true
}
logger.Warn(err)
func FileExist(path string) bool {
_, err := os.Stat(path)
if err == nil || os.IsExist(err) {
return true
}
return false
}
+195
View File
@@ -0,0 +1,195 @@
// Copyright © 2021 sealos.
//
// 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 utils
import (
"bufio"
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/fanux/sealos/pkg/utils/logger"
)
type HostFile struct {
Path string
Hosts map[string]*Hostname
}
type Hostname struct {
Comment string
Domain string
IP string
}
func NewHostname(comment string, domain string, ip string) *Hostname {
return &Hostname{comment, domain, ip}
}
func (h *HostFile) Add(host *Hostname) {
if h.Hosts == nil {
h.Hosts = make(map[string]*Hostname)
}
h.Hosts[host.Domain] = host
}
func (h *HostFile) Delete(host string) {
delete(h.Hosts, host)
}
func getHostPath() string {
path := os.Getenv("GOHOST_FILE")
if path == "" {
path = "/etc/hosts"
}
return path
}
func (h *Hostname) toString() string {
if len(h.Comment) > 0 {
h.Comment += "\n"
}
return h.Comment + h.IP + " " + h.Domain + "\n"
}
func appendToFile(filePath string, hostname *Hostname) {
fp, err := os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
logger.Warn("failed opening file %s : %s\n", filePath, err)
return
}
defer fp.Close()
_, err = fp.WriteString(hostname.toString())
if err != nil {
logger.Warn("failed append string: %s: %s\n", filePath, err)
return
}
}
func (h *HostFile) ParseHostFile(path string) (map[string]*Hostname, error) {
if !FileExist(path) {
logger.Warn("path %s is not exists", path)
return nil, errors.New("path %s is not exists")
}
fp, fpErr := os.Open(path)
if fpErr != nil {
logger.Warn("open file '%s' failed\n", path)
return nil, fmt.Errorf("open file '%s' failed ", path)
}
defer fp.Close()
br := bufio.NewReader(fp)
hostnameMap := make(map[string]*Hostname)
curComment := ""
for {
str, rErr := br.ReadString('\n')
if rErr == io.EOF {
break
}
if len(str) == 0 || str == "\r\n" || IsEmptyLine(str) {
continue
}
if str[0] == '#' {
// 处理注释
curComment += str
continue
}
tmpHostnameArr := strings.Fields(str)
curDomain := TrimWS(tmpHostnameArr[1])
if !CheckDomain(curDomain) {
return hostnameMap, errors.New(" file contain error domain" + curDomain)
}
curIP := TrimWS(tmpHostnameArr[0])
checkIP := CheckIP(curIP)
if !checkIP {
return hostnameMap, nil
}
tmpHostname := NewHostname(curComment, curDomain, curIP)
hostnameMap[tmpHostname.Domain] = tmpHostname
curComment = ""
}
return hostnameMap, nil
}
func (h *HostFile) AppendHost(domain string, ip string) {
if domain == "" || ip == "" {
return
}
hostname := NewHostname("", domain, ip)
appendToFile(getHostPath(), hostname)
}
func (h *HostFile) writeToFile(hostnameMap map[string]*Hostname, path string) {
if !FileExist(path) {
logger.Warn("path %s is not exists", path)
return
}
fp, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
if err != nil {
logger.Warn("open file '%s' failed: %v", path, err)
return
}
defer fp.Close()
for _, mapVal := range hostnameMap {
_, writeErr := fp.WriteString(mapVal.toString())
if writeErr != nil {
logger.Warn(writeErr)
return
}
}
}
func (h *HostFile) DeleteDomain(domain string) {
if domain == "" {
return
}
currHostsMap, parseErr := h.ParseHostFile(getHostPath())
if parseErr != nil {
logger.Warn("parse file failed" + parseErr.Error())
return
}
if len(currHostsMap) == 0 || currHostsMap[domain] == nil {
logger.Warn("domain %s not exist\n", domain)
return
}
delete(currHostsMap, domain)
h.writeToFile(currHostsMap, getHostPath())
}
func (h *HostFile) ListCurrentHosts() {
currHostsMap, parseErr := h.ParseHostFile(getHostPath())
if parseErr != nil {
logger.Warn("parse file failed" + parseErr.Error())
return
}
if len(currHostsMap) == 0 {
return
}
for _, mapVal := range currHostsMap {
fmt.Println(mapVal.toString())
}
}
+1 -1
View File
@@ -32,7 +32,7 @@ func DownloadFile(location string) (filePATH, md5 string) {
location = absPATH
}
//file md5
md5 = FromLocal(location)
md5 = Md5File(location)
return location, md5
}
+28 -13
View File
@@ -20,7 +20,7 @@ import (
"fmt"
"math/big"
"net"
"os"
"net/url"
"strings"
"github.com/fanux/sealos/pkg/utils/logger"
@@ -87,24 +87,34 @@ func AssemblyIPList(args *string) error {
*args = result
return nil
}
func CheckIP(i string) bool {
if !strings.Contains(i, ":") {
return net.ParseIP(i) != nil
}
if _, err := net.ResolveTCPAddr("tcp", i); err != nil {
func CheckIP(ipStr string) bool {
host, _, err := net.SplitHostPort(ipStr)
if err != nil {
return false
}
return true
ip := net.ParseIP(host)
if _, err = net.ResolveTCPAddr("tcp", ipStr); err != nil {
return false
}
return ip != nil
}
//IPFormat is
func IPFormat(host string) string {
ipAndPort := strings.Split(host, ":")
if len(ipAndPort) != 2 {
logger.Error("invalied host fomat [%s], must like 172.0.0.2:22", host)
os.Exit(1)
func IPFormat(ipStr string) string {
ip := net.ParseIP(ipStr)
if ip == nil {
host, _, err := net.SplitHostPort(ipStr)
if err != nil {
logger.Error("invalied host fomat [%s], must like 172.0.0.2:22.error: %s", ipStr, err)
return ""
}
ip = net.ParseIP(host)
}
return ipAndPort[0]
if ip == nil {
logger.Error("invalied host fomat [%s], must like 172.0.0.2:22", ipStr)
return ""
}
return ip.String()
}
func HostnameAndIP(node []string) ([]string, []string) {
@@ -166,3 +176,8 @@ func RemoveDeduplicate(a []string) []string {
}
return res
}
func CheckDomain(domain string) bool {
_, errURL := url.Parse(domain)
return errURL == nil
}
-1
View File
@@ -41,7 +41,6 @@ func (c *connLogger) Init(jsonConfig string) error {
if len(jsonConfig) == 0 {
return nil
}
fmt.Printf("consoleWriter Init:%s\n", jsonConfig)
err := json.Unmarshal([]byte(jsonConfig), c)
if err != nil {
return err
-2
View File
@@ -16,7 +16,6 @@ package logger
import (
"encoding/json"
"fmt"
"os"
"runtime"
"sync"
@@ -59,7 +58,6 @@ func (c *consoleLogger) Init(jsonConfig string) error {
if len(jsonConfig) == 0 {
return nil
}
fmt.Printf("consoleWriter Init:%s\n", jsonConfig)
err := json.Unmarshal([]byte(jsonConfig), c)
if runtime.GOOS == "windows" {
c.Colorful = false
+1 -1
View File
@@ -153,7 +153,7 @@ func (r *LocalLogger) SetLogger(adapterName string, configs ...string) error {
}
config := append(configs, "{}")[0]
var num int = -1
var num = -1
var i int
var l *nameLogger
for i, l = range r.outputs {
+33
View File
@@ -0,0 +1,33 @@
// Copyright © 2021 Alibaba Group Holding Ltd.
//
// 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 utils
import (
"fmt"
"time"
)
func Retry(tryTimes int, trySleepTime time.Duration, action func() error) error {
var err error
for i := 0; i < tryTimes; i++ {
err = action()
if err == nil {
return nil
}
time.Sleep(trySleepTime * time.Duration(2*i+1))
}
return fmt.Errorf("retry action timeout: %v", err)
}
+3 -3
View File
@@ -39,7 +39,7 @@ func (ss *SSH) CopyForMD5(host, localFilePath, remoteFilePath, md5 string) bool
//如果有md5则可以验证
//如果没有md5则拿到本地数据后验证
if md5 == "" {
md5 = utils.FromLocal(localFilePath)
md5 = utils.Md5File(localFilePath)
}
logger.Debug("[ssh]source file md5 value is %s", md5)
ss.Copy(host, localFilePath, remoteFilePath)
@@ -362,7 +362,7 @@ func (ss *SSH) copyLocalFileToRemote(host string, sshClient *ssh.Client, sftpCli
func (ss *SSH) isCopyMd5Success(sshClient *ssh.Client, localFile, remoteFile string) bool {
cmd := fmt.Sprintf("md5sum %s | cut -d\" \" -f1", remoteFile)
localMd5 := utils.FromLocal(localFile)
localMd5 := utils.Md5File(localFile)
sshSession, err := sshClient.NewSession()
if err != nil {
return false
@@ -399,7 +399,7 @@ func (ss *SSH) isCopyMd5Success(sshClient *ssh.Client, localFile, remoteFile str
}
func (ss *SSH) ValidateMd5sumLocalWithRemote(host, localFile, remoteFile string) bool {
localMd5 := utils.FromLocal(localFile)
localMd5 := utils.Md5File(localFile)
return localMd5 == ss.Md5Sum(host, remoteFile)
}
+3 -1
View File
@@ -14,7 +14,9 @@
package ssh
import "time"
import (
"time"
)
type SSH struct {
User string
+12 -1
View File
@@ -20,6 +20,7 @@ import (
"bytes"
"math/rand"
"net"
"regexp"
"sort"
"strings"
"time"
@@ -110,7 +111,7 @@ func ContainList(list []string, toComplete string) (containerList []string) {
// RandString 生成随机字符串
func RandString(len int) string {
var r *rand.Rand = rand.New(rand.NewSource(time.Now().Unix()))
var r = rand.New(rand.NewSource(time.Now().Unix()))
bytes := make([]byte, len)
for i := 0; i < len; i++ {
b := r.Intn(26) + 65
@@ -118,3 +119,13 @@ func RandString(len int) string {
}
return string(bytes)
}
func IsEmptyLine(str string) bool {
re := regexp.MustCompile(`^\s*$`)
return re.MatchString(str)
}
func TrimWS(str string) string {
return strings.Trim(str, " \n\t")
}
+30
View File
@@ -108,3 +108,33 @@ func VersionToIntAll(version string) int {
}
return 0
}
//VersionCompare is version compare
// if v1 >= v2 return true, else return false
func VersionCompare(v1, v2 string) bool {
v1 = strings.Replace(v1, "v", "", -1)
v2 = strings.Replace(v2, "v", "", -1)
v1 = strings.Split(v1, "-")[0]
v2 = strings.Split(v2, "-")[0]
v1List := strings.Split(v1, ".")
v2List := strings.Split(v2, ".")
if len(v1List) != 3 || len(v2List) != 3 {
logger.Error("error version format %s %s", v1, v2)
return false
}
if v1List[0] > v2List[0] {
return true
} else if v1List[0] < v2List[0] {
return false
}
if v1List[1] > v2List[1] {
return true
} else if v1List[1] < v2List[1] {
return false
}
if v1List[2] > v2List[2] {
return true
}
return true
}
+52
View File
@@ -0,0 +1,52 @@
// Copyright © 2021 sealos.
//
// 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 version
// Base version information.
//
// This is the fallback data used when version information from git is not
// provided via go ldflags. It provides an approximation of the Kubernetes
// version for ad-hoc builds (e.g. `go build`) that cannot get the version
// information from git.
//
// If you are looking at these fields in the git tree, they look
// strange. They are modified on the fly by the build process. The
// in-tree values are dummy values used for "git archive", which also
// works for GitHub tar downloads.
//
// When releasing a new Kubernetes version, this file is updated by
// build/mark_new_version.sh to reflect the new version, and then a
// git annotated tag (using format vX.Y where X == Major version and Y
// == Minor version) is created to point to the commit that updates
// component-base/version/base.go
var (
// semantic version, derived by build scripts (see
// https://github.com/kubernetes/community/blob/master/contributors/design-proposals/release/versioning.md
// for a detailed discussion of this field)
//
// TODO: This field is still called "gitVersion" for legacy
// reasons. For prerelease versions, the build metadata on the
// semantic version is a git hash, but the version itself is no
// longer the direct output of "git describe", but a slight
// translation to be semver compliant.
// NOTE: The $Format strings are replaced during 'git archive' thanks to the
// companion .gitattributes file containing 'export-subst' in this same
// directory. See also https://git-scm.com/docs/gitattributes
gitVersion = "latest"
gitCommit = "" // sha1 from git, output of $(git rev-parse HEAD)
buildDate = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
)
+32
View File
@@ -0,0 +1,32 @@
// Copyright © 2021 sealos.
//
// 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 version
// Info contains versioning information.
// TODO: Add []string of api versions supported? It's still unclear
// how we'll want to distribute that information.
type Info struct {
GitVersion string `json:"gitVersion"`
GitCommit string `json:"gitCommit,omitempty"`
BuildDate string `json:"buildDate"`
GoVersion string `json:"goVersion"`
Compiler string `json:"compiler"`
Platform string `json:"platform"`
}
// String returns info as a human-friendly version string.
func (info Info) String() string {
return info.GitVersion
}
+35
View File
@@ -0,0 +1,35 @@
// Copyright © 2021 sealos.
//
// 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 version
import (
"fmt"
"runtime"
)
// Get returns the overall codebase version. It's for detecting
// what code a binary was built from.
func Get() Info {
// These variables typically come from -ldflags settings and in
// their absence fallback to the settings in ./base.go
return Info{
GitVersion: gitVersion,
GitCommit: gitCommit,
BuildDate: buildDate,
GoVersion: runtime.Version(),
Compiler: runtime.Compiler,
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
}
}