diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..7a4a3ea2424c09fbe48d455aed1eaa94d9124835 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. \ No newline at end of file diff --git a/README.md b/README.md index fbc0f5ee26510d3d7e37fb07d90b17e865b23b30..ee125ecb592a0c54a77d157e28e36ebafb43121e 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,30 @@ # nestos-kubernetes-deployer #### 介绍 -A Nestos based kubernetes deployment tool +nestos-kubernetes-deployer简称NKD,是基于NestOS部署kubernetes集群运维而准备的解决方案。其目标是在集群外提供对集群基础设施(包括操作系统和kubernetes基础组件)的部署、更新和配置管理等服务,从而简化了集群部署和升级的流程。 + +#### 支持平台 +NKD根据集群需求,连接基础设施提供商动态创建所需的IaaS资源,支持裸金属和虚拟化场景,目前优先实现openstack场景。 #### 软件架构 -软件架构说明 +整体架构如图: +![arch](/docs/images/arch.jpg) +NKD的整体架构由多个组件构成,主要包括NKDS(NestOS-kubernetes-deployer-service)作为主体、部署到集群中的HKO(housekeeper operator)以及集成在NestOS镜像中的installer。此外,还可以配合NestOS镜像构建工具链、配置管理仓库(如git)和私有化部署的容器镜像仓库,共同完成集群运维任务。目前NKDS以命令行工具提供,暂不提供对外http接口和前端配置页面,但主体功能所需的基础设施管理、配置管理、系统镜像管理、证书管理、健康检测等模块已初步形成。HKO主要包括面向集群的HKO组件和集成在NestOS镜像中的HKD(housekeeper daemon)组件。目前installer组件负责在系统点火阶段部署创建K8S集群,未来计划将其功能融合到HKD组件中,使整体方案更加精简,更易于用户根据个性化需求管理所需的K8S基础组件。 -#### 安装教程 +#### 安装部署 +NKD目前提供工具形式,仅支持通过命令行参数或应用配置文件部署kubernetes集群。后续会提供用户友好的前端配置界面,便于轻松生成所需配置,并提供配置变更版本管理功能。在部署NestOS系统时,需要通过ignition点火机制传入系统部署后所需的动态配置。NKD会将用户提供的kubernetes集群部署所需的配置自动合并到ign文件中,使得节点在部署完成操作系统后引导自动开始创建k8s集群,无需手动干预。 -1. xxxx -2. xxxx -3. xxxx +#### 升级维护 +NKD提供了操作系统或k8s基础组件升级维护的功能。用户可以选择是否部署housekeeper自定义资源,用于后续的维护升级。housekeeper的主要更新流程是当操作系统或k8s基础组件需要升级维护时,NKD使用镜像构建工具重新构建新版系统镜像,并在查询到新版镜像后,向集群创建housekeeper CR资源。集群中的housekeeper服务按照配置逐次对集群节点进行升级,完成整个集群的升级工作。 -#### 使用说明 +#### 未来规划 +NKD的最终目标是以长期驻留服务形式提供运维服务,同时支持多个集群的管理。它将提供持久化配置变更记录、证书管理、多种更新升级策略和镜像源频道等功能。未来,我们将持续优化NKD的功能和性能,并引入更多智能化特性,如自动化故障处理和资源优化等。我们的目标是将NKD打造成NestOS生态中的核心组件,为云原生场景下的运维工作提供全方位支持,进一步推动云原生技术的发展和应用。 -1. xxxx -2. xxxx -3. xxxx #### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 -4. 新建 Pull Request - - -#### 特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +4. 新建 Pull Request \ No newline at end of file diff --git a/cmd/nkd/deploy.go b/cmd/nkd/deploy.go new file mode 100755 index 0000000000000000000000000000000000000000..0caebe63c239f85b536a71899b642aef9bf6cd3a --- /dev/null +++ b/cmd/nkd/deploy.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/spf13/cobra" +) + +var ( + deployCommand = &cobra.Command{ + Use: "deploy [nodes]", + Short: "deploy masters ", + Long: "deploy cluster bootconfig for a new cluster", + RunE: deploy, + Args: cobra.MaximumNArgs(1), + } +) + + +func deploy(command *cobra.Command, args []string) error{ + println("deploy") + return nil +} diff --git a/cmd/nkd/generate.go b/cmd/nkd/generate.go new file mode 100755 index 0000000000000000000000000000000000000000..961909c41ad63d1f001a19facd4c589fbd3eb0cf --- /dev/null +++ b/cmd/nkd/generate.go @@ -0,0 +1,20 @@ +package main + +import "github.com/spf13/cobra" + +var ( + generateCommand = &cobra.Command{ + Use: "generate [options] [REGISTRY]", + Short: "generate cluster bootconfig", + Long: "generate cluster bootconfig for a new cluster", + RunE: generate, + Args: cobra.MaximumNArgs(1), + } +) + + +func generate(command *cobra.Command, args []string) error{ + println("generate") + return nil +} + diff --git a/cmd/nkd/join.go b/cmd/nkd/join.go new file mode 100755 index 0000000000000000000000000000000000000000..1a7abf920bdab998b10fce4a43b515075cce4586 --- /dev/null +++ b/cmd/nkd/join.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/spf13/cobra" +) + +var ( + joinCommand = &cobra.Command{ + Use: "join [nodes]", + Short: "join nodes ", + Long: "join nodes to cluster", + RunE: join, + Args: cobra.MaximumNArgs(1), + } +) + + +func join(command *cobra.Command, args []string) error{ + println("join") + return nil +} diff --git a/cmd/nkd/main.go b/cmd/nkd/main.go new file mode 100755 index 0000000000000000000000000000000000000000..e4eaf8e639a46a1472b8c24b3d6fbf71075151a1 --- /dev/null +++ b/cmd/nkd/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "os" + "path/filepath" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +func newRootCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: filepath.Base(os.Args[0]), + Short: "Create kubernetes cluster", + Long: "", + } + return cmd +} + +func main() { + rootCmd := newRootCmd() + + for _, subCmd := range []*cobra.Command{ + /*todo: + newDeployCmd() + newGenerateCmd() + newInitCmd() + newJoinCommand() + */ + newUpgradeCmd(), + } { + rootCmd.AddCommand(subCmd) + } + if err := rootCmd.Execute(); err != nil { + logrus.Errorf("Error executing nkd: %v", err) + } +} diff --git a/cmd/nkd/root.go b/cmd/nkd/root.go new file mode 100755 index 0000000000000000000000000000000000000000..d8a4995be3b602ac0e31747bfeb3e5985552280c --- /dev/null +++ b/cmd/nkd/root.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/spf13/cobra" +) + +var ( + rootCmd = &cobra.Command{ + Use: "nkd [options]", + Long: "deploy k8s clusters", + SilenceUsage: true, + SilenceErrors: true, + TraverseChildren: true, + //PersistentPreRunE: persistentPreRunE, + //RunE: validate.SubCommandExists, + //PersistentPostRunE: persistentPostRunE, + //Version: version.Version.String(), + DisableFlagsInUseLine: true, + } + +) diff --git a/cmd/nkd/upgrade.go b/cmd/nkd/upgrade.go new file mode 100755 index 0000000000000000000000000000000000000000..8493cacd6081e8c44339ac2c29a8393e03ce885e --- /dev/null +++ b/cmd/nkd/upgrade.go @@ -0,0 +1,118 @@ +package main + +import ( + "context" + "fmt" + "time" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/yaml" + + wait "k8s.io/apimachinery/pkg/util/wait" +) + +func newUpgradeCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "upgrade", + Short: "Upgrade your cluster to a newer version", + Long: "", + RunE: runUpgradeCmd, + } + return cmd +} + +func runUpgradeCmd(command *cobra.Command, args []string) error { + var ( + osVersion = "" + osImageURL = "" + kubeVersion = "" + evictPodForce = false + maxUnavailable = 2 + loopTimeout = 2 * time.Minute + kubeconfig = "" + ) + // Get the kubeconfig configuration + config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + config, err = rest.InClusterConfig() + if err != nil { + logrus.Errorf("Error getting Kubernetes client config: %v\n", err) + return err + } + } + + // Create dynamic client + dynamicClient, err := dynamic.NewForConfig(config) + if err != nil { + logrus.Errorf("Error creating Dynamic client: %v\n", err) + return err + } + + // Define the YAML data for the Custom Resource (CR) + yamlData := fmt.Sprintf(` +apiVersion: housekeeper.io/v1alpha1 +kind: Update +metadata: + name: update-sample + namespace: housekeeper-system +spec: + osVersion: %s + osImageURL: %s + kubeVersion: %s + evictPodForce: %t + maxUnavailable: %d +`, osVersion, osImageURL, kubeVersion, evictPodForce, maxUnavailable) + + var unstructuredObj unstructured.Unstructured + err = yaml.Unmarshal([]byte(yamlData), &unstructuredObj) + if err != nil { + logrus.Errorf("Error unmarshalling YAML: %v\n", err) + return err + } + + // Create or Update CR + resource := schema.GroupVersionResource{ + Group: "housekeeper.io", + Version: "v1alpha1", + Resource: "updates", // Pluralized resource name + } + + // The loop attempts to create or update a CR until it succeeds or times out + if err := wait.PollImmediate(2*time.Second, loopTimeout, func() (bool, error) { + gvk := unstructuredObj.GroupVersionKind() + dynamicResource := dynamicClient.Resource(gvk.GroupVersion().WithResource(resource.Resource)).Namespace(unstructuredObj.GetNamespace()) + + //Attempts to get the specified Custom Resource from the Kubernetes API Server. + obj, err := dynamicResource.Get(context.Background(), unstructuredObj.GetName(), metav1.GetOptions{}) + if err != nil { + // Not found, create the resource + _, err = dynamicResource.Create(context.Background(), &unstructuredObj, metav1.CreateOptions{}) + if err == nil { + logrus.Infof("Custom Resource created successfully!") + return true, nil + } + } else { + // Found, update the resource + unstructuredObj.SetResourceVersion(obj.GetResourceVersion()) + _, err = dynamicResource.Update(context.Background(), &unstructuredObj, metav1.UpdateOptions{}) + if err == nil { + logrus.Infof("Custom Resource updated successfully!") + return true, nil + } + } + logrus.Errorf("Error creating or updating CR: %v\n", err) + return false, nil + }); err != nil { + logrus.Errorf("Timeout while waiting for Custom Resource to be created or updated.") + } + + return nil +} diff --git a/data/templates/openstack/main.tf.template b/data/templates/openstack/main.tf.template new file mode 100644 index 0000000000000000000000000000000000000000..ec69fa51e3c93f2d2fb2ccd6ff18580f77275aae --- /dev/null +++ b/data/templates/openstack/main.tf.template @@ -0,0 +1,95 @@ +terraform { + required_providers { + openstack = { + source = "terraform-provider-openstack/openstack" + version = "1.52.1" + } + } +} + +provider "openstack" { + user_name = "{{.Openstack.User_name}}" + password = "{{.Openstack.Password}}" + tenant_name = "{{.Openstack.Tenant_name}}" + auth_url = "{{.Openstack.Auth_url}}" + region = "{{.Openstack.Region}}" +} + +resource "openstack_images_image_v2" "image" { + name = "{{.Image.Name}}" + + image_source_url = "{{.Image.Image_source_url}}" + image_source_username = "{{.Image.Image_source_username}}" + image_source_password = "{{.Image.Image_source_password}}" + web_download = "{{.Image.Web_download}}" + + local_file_path = "{{.Image.local_file_path}}" + + container_format = "{{.Image.Container_format}}" + disk_format = "{{.Image.Disk_format}}" +} + +resource "openstack_compute_flavor_v2" "flavor" { + name = "{{.Flavor.Name}}" + ram = "{{.Flavor.Ram}}" + vcpus = "{{.Flavor.Vcpus}}" + disk = "{{.Flavor.Disk}}" + is_public = "{{.Flavor.Is_public}}" +} + +resource "openstack_compute_keypair_v2" "keypair" { + name = "{{.Keypair.Name}}" + public_key = "{{.Keypair.Public_key}}" +} + +resource "openstack_compute_secgroup_v2" "secgroup" { + name = "{{.Secgroup.Name}}" + description = "{{.Secgroup.Name}}" + + rule { + from_port = 22 + to_port = 22 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = -1 + to_port = -1 + ip_protocol = "icmp" + cidr = "0.0.0.0/0" + } +} + +resource "openstack_compute_instance_v2" "instance" { + count = "{{.Instance.Count}}" + name = "{{.Instance.Name}}" + image_name = openstack_images_image_v2.image.name + flavor_name = openstack_compute_flavor_v2.flavor.name + key_pair = openstack_compute_keypair_v2.keypair.name + security_groups = [openstack_compute_secgroup_v2.secgroup.name] + user_data = "{{.Instance.User_data}}" + + network { + name = "{{.Instance.Network.Name}}" + } +} + +resource "openstack_networking_floatingip_v2" "floatip" { + count = length(openstack_compute_instance_v2.instance) + pool = "{{.Floatip.Pool}}" +} + +resource "openstack_compute_floatingip_associate_v2" "fip_associate" { + count = length(openstack_compute_instance_v2.instance) + floating_ip = openstack_networking_floatingip_v2.floatip.*.address[count.index] + instance_id = openstack_compute_instance_v2.instance.*.id[count.index] +} + +output "instance_info" { + value = { + instance_status = openstack_compute_instance_v2.instance.*.power_state + access_ip_v4 = openstack_compute_instance_v2.instance.*.access_ip_v4 + floating_ip = openstack_networking_floatingip_v2.floatip.*.address + } +} diff --git a/docs/images/arch.jpg b/docs/images/arch.jpg new file mode 100644 index 0000000000000000000000000000000000000000..24afd47bbbf87077a4279ce3bdf9d33e39dde4e8 Binary files /dev/null and b/docs/images/arch.jpg differ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..4df5559d5ad41ed7e1a5576aa7b0a99942c68242 --- /dev/null +++ b/go.mod @@ -0,0 +1,48 @@ +module gitee.com/openeuler/nestos-kubernetes-deployer + +go 1.19 + +require ( + github.com/hashicorp/terraform-exec v0.18.1 + github.com/lithammer/dedent v1.1.0 + github.com/openshift/installer v0.16.1 + github.com/pkg/errors v0.9.1 + github.com/sirupsen/logrus v1.9.3 + github.com/spf13/cobra v1.7.0 +) + +require ( + github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gofuzz v1.1.0 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/terraform-json v0.16.0 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/zclconf/go-cty v1.13.1 // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/term v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/apimachinery v0.27.4 // indirect + k8s.io/client-go v0.27.4 // indirect + k8s.io/klog/v2 v2.90.1 // indirect + k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..5062ad1f777299305e8ca25140c0b953f4628ae9 --- /dev/null +++ b/go.sum @@ -0,0 +1,480 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= +github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= +github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= +github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= +github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hc-install v0.5.0 h1:D9bl4KayIYKEeJ4vUDe9L5huqxZXczKaykSRcmQ0xY0= +github.com/hashicorp/terraform-exec v0.18.1 h1:LAbfDvNQU1l0NOQlTuudjczVhHj061fNX5H8XZxHlH4= +github.com/hashicorp/terraform-exec v0.18.1/go.mod h1:58wg4IeuAJ6LVsLUeD2DWZZoc/bYi6dzhLHzxM41980= +github.com/hashicorp/terraform-json v0.16.0 h1:UKkeWRWb23do5LNAFlh/K3N0ymn1qTOO8c+85Albo3s= +github.com/hashicorp/terraform-json v0.16.0/go.mod h1:v0Ufk9jJnk6tcIZvScHvetlKfiNTC+WS21mnXIlc0B0= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/openshift/installer v0.16.1 h1:PmjALN9x1NVNVi3SCqfz0ZwVCgOkQLQWo2nHYXREq/A= +github.com/openshift/installer v0.16.1/go.mod h1:VWGgpJgF8DGCKQjbccnigglhZnHtRLCZ6cxqkXN4Ck0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zclconf/go-cty v1.13.1 h1:0a6bRwuiSHtAmqCqNOE+c2oHgepv0ctoxU4FUe43kwc= +github.com/zclconf/go-cty v1.13.1/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/apimachinery v0.27.4 h1:CdxflD4AF61yewuid0fLl6bM4a3q04jWel0IlP+aYjs= +k8s.io/apimachinery v0.27.4/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= +k8s.io/client-go v0.27.4 h1:vj2YTtSJ6J4KxaC88P4pMPEQECWMY8gqPqsTgUKzvjk= +k8s.io/client-go v0.27.4/go.mod h1:ragcly7lUlN0SRPk5/ZkGnDjPknzb37TICq07WhI6Xc= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= +k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/housekeeper/Makefile b/housekeeper/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..697297154a60aa392b5dcf679ecee0668a94466f --- /dev/null +++ b/housekeeper/Makefile @@ -0,0 +1,85 @@ +# Copyright 2023 KylinSoft Co., 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. + +IMG_OPERATOR ?= housekeeper-operator:latest +IMG_CONTROLLER ?= housekeeper-controller:latest + +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +CONTROLLER_TOOLS_VERSION ?= v0.9.2 + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +# Setting SHELL to bash allows bash commands to be executed by recipes. +# Options are set to exit when a recipe line exits non-zero or a piped command fails. +SHELL = /usr/bin/env bash -o pipefail +.SHELLFLAGS = -ec + + +##@ Build +.PHONY: all +all: housekeeper-operator-manager housekeeper-controller-manager housekeeper-daemon +# Build binary +housekeeper-operator-manager: + go build -o bin/housekeeper-operator-manager operator/housekeeper-operator/main.go +housekeeper-controller-manager: + go build -o bin/housekeeper-controller-manager operator/housekeeper-controller/main.go +housekeeper-daemon: + go build -o bin/housekeeper-daemon daemon/main.go + +# Build the docker image +.PHONY: docker-build +docker-build: ## Build docker image with the housekeeper-operator-manager. + docker build -t ${IMG_OPERATOR} . + docker build -t ${IMG_CONTROLLER} . + +.PHONY: docker-push +docker-push: ## Push docker image with the housekeeper-operator-manager. + docker push ${IMG_OPERATOR} + docker push ${IMG_CONTROLLER} + +# ##@ Development +.PHONY: manifests +manifests: controller-gen ##Generate manifests e.g. CRD/RBAC + $(CONTROLLER_GEN) rbac:roleName=update-manager-role crd paths="./..." output:crd:artifacts:config=config/crd + +.PHONY: install +install: ## Install CRD in a cluster + kubectl apply -f config/crd + +.PHONY: uninstall +uninstall: ## Uninstall CRD from a cluster + kubectl delete -f config/crd + +.PHONY: deploy +deploy: ## Deploy controller + kubectl apply -f config/rbac + kubectl apply -f config/manager + +.PHONY: undeploy +undeploy: ## Undeploy controller + kubectl delete -f config/rbac + kubectl delete -f config/manager + +## Location to install dependencies to +.PHONY: controller-gen +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. +$(CONTROLLER_GEN): $(LOCALBIN) + test -s $(LOCALBIN)/controller-gen || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) diff --git a/housekeeper/daemon/main.go b/housekeeper/daemon/main.go new file mode 100644 index 0000000000000000000000000000000000000000..c843375b0ee1ca947cd1d6679d2f1a0673952138 --- /dev/null +++ b/housekeeper/daemon/main.go @@ -0,0 +1,33 @@ +/* +Copyright 2023 KylinSoft Co., 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 main + +import ( + "os" + + "github.com/sirupsen/logrus" + "housekeeper.io/daemon/server" + "housekeeper.io/pkg/version" + _ "k8s.io/client-go/plugin/pkg/client/auth" +) + +func main() { + logrus.Info("Version is:", version.Version) + if err := server.Run(); err != nil { + logrus.Errorln("listen error" + err.Error()) + os.Exit(1) + } +} diff --git a/housekeeper/daemon/server/listener.go b/housekeeper/daemon/server/listener.go new file mode 100644 index 0000000000000000000000000000000000000000..007ef7fe9d75f38b432642fb4d88428375eaca0c --- /dev/null +++ b/housekeeper/daemon/server/listener.go @@ -0,0 +1,75 @@ +/* +Copyright 2023 KylinSoft Co., 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 server + +import ( + "fmt" + "net" + "os" + "path/filepath" + "syscall" + + "github.com/sirupsen/logrus" + "google.golang.org/grpc" + pb "housekeeper.io/pkg/connection/proto" + "housekeeper.io/pkg/constants" +) + +func NewListener(dir, name string) (l net.Listener, err error) { + if err := os.MkdirAll(dir, 0750); err != nil { + return nil, err + } + + addr := filepath.Join(dir, name) + gid := os.Getgid() + if err = syscall.Unlink(addr); err != nil && !os.IsNotExist(err) { + return nil, err + } + + const socketPermission = 0640 + mask := syscall.Umask(^socketPermission & int(os.ModePerm)) + defer syscall.Umask(mask) + + l, err = net.Listen("unix", addr) + if err != nil { + return nil, err + } + + if err := os.Chown(addr, 0, gid); err != nil { + if err := l.Close(); err != nil { + return nil, fmt.Errorf("close listener error %w", err) + } + return nil, err + } + return l, nil +} + +func Run() error { + lis, err := NewListener(constants.SockDir, constants.SockName) + if err != nil { + logrus.Errorf("listen error: %v", err) + return err + } + //get grpc server + s := grpc.NewServer() + pb.RegisterUpgradeClusterServer(s, &Server{}) + logrus.Info("housekeeper-daemon start serving") + if err := s.Serve(lis); err != nil { + logrus.Errorf("housekeeper-daemon server error: %v", err) + return err + } + return nil +} diff --git a/housekeeper/daemon/server/server.go b/housekeeper/daemon/server/server.go new file mode 100644 index 0000000000000000000000000000000000000000..faa24e28405265316a06ac620a6e38ebaaba8f1c --- /dev/null +++ b/housekeeper/daemon/server/server.go @@ -0,0 +1,208 @@ +/* +Copyright 2023 KylinSoft Co., 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 server + +import ( + "context" + "fmt" + "os" + "os/exec" + "strings" + "sync" + + "github.com/sirupsen/logrus" + "housekeeper.io/pkg/common" + pb "housekeeper.io/pkg/connection/proto" + utilVersion "k8s.io/apimachinery/pkg/util/version" +) + +const ( + ostreeImage = "ostree-unverified-image:docker://" + kubeadmCmd = "/usr/local/bin/kubeadm" + upgradeMasterCmd = "/usr/local/bin/kubeadm upgrade apply -y" + upgradeWorkerCmd = "/usr/local/bin/kubeadm upgrade node" + kubeletUpdateCmd = "systemctl daemon-reload && systemctl restart kubelet" + adminFile = "/etc/kubernetes/admin.conf" +) + +type Server struct { + pb.UnimplementedUpgradeClusterServer + mu sync.Mutex +} + +// Implements the Upgrade +func (s *Server) Upgrade(_ context.Context, req *pb.UpgradeRequest) (*pb.UpgradeResponse, error) { + s.mu.Lock() + defer s.mu.Unlock() + + // upgrade os + if len(req.OsVersion) > 0 { + //Checking for os version + if err := checkOsVersion(req); err != nil { + return &pb.UpgradeResponse{}, err + } + } + // upgrade kubernetes + if len(req.KubeVersion) > 0 { + markFile := fmt.Sprintf("%s%s%s", "/var/housekeeper/", req.KubeVersion, ".stamp") + if common.IsFileExist(markFile) { + return &pb.UpgradeResponse{}, nil + } + //Checking for kubernetes version + if err := checkKubeVersion(req); err != nil { + return &pb.UpgradeResponse{}, err + } + // todo: check upgrade successfully + if err := markNode(markFile); err != nil { + logrus.Errorf("failed to mark node: %v", err) + return &pb.UpgradeResponse{}, err + } + } + return &pb.UpgradeResponse{}, nil +} + +func checkOsVersion(req *pb.UpgradeRequest) error { + args := []string{"-c", "cat /etc/os-release | grep 'VERSION=' | head -n 1 | awk -F 'VERSION=' '{print $2}'"} + osVersion, err := runCmd("/bin/sh", args...) + if err != nil { + logrus.Errorf("failed to get os version: %v", err) + return err + } + if cmp, err := utilVersion.MustParseSemantic(string(osVersion)).Compare(req.OsVersion); err != nil { + logrus.Errorf("failed to parse os version: %v", err) + return err + } else if cmp == 0 { + logrus.Infof("The current os version %s and the desired upgrade version %s are the same", + string(osVersion), req.OsVersion) + return nil + } + //Compare the current os version with the desired version. + //If different, the update command is executed + if err := upgradeOSVersion(req); err != nil { + logrus.Errorf("upgrade os version error: %v", err) + return err + } + return nil +} + +func checkKubeVersion(req *pb.UpgradeRequest) error { + args := []string{"version", "-o", "short"} + kubeadmVersion, err := runCmd(kubeadmCmd, args...) + if err != nil { + logrus.Errorf("kubeadm get version failed: %v", err) + return err + } + if cmp, err := utilVersion.MustParseSemantic(string(kubeadmVersion)).Compare(req.KubeVersion); err != nil { + logrus.Errorf("failed to parse kubeadm version: %v", err) + return err + } else if cmp == -1 { + logrus.Infof("The request upgraded version %s is larger than kubeadm's version %s", + req.KubeVersion, string(kubeadmVersion)) + return nil + } + //If the version of kubeadm is not less than the version requested for upgrade, + //the upgrade command is executed + if err := upgradeKubeVersion(req); err != nil { + logrus.Errorf("upgrade kubernetes version error: %v", err) + return err + } + return nil +} + +func upgradeOSVersion(req *pb.UpgradeRequest) error { + //upgrade os + customImageURL := fmt.Sprintf("%s%s", ostreeImage, req.OsImageUrl) + args := []string{"rebase", "--experimental", customImageURL, "--bypass-driver"} + if _, err := runCmd("rpm-ostree", args...); err != nil { + logrus.Errorf("failed to upgrade os to %s : %w", req.OsVersion, err) + return err + } + // todo:skipping restart system + if err := exec.Command("/bin/sh", "-c", "systemctl reboot").Run(); err != nil { + logrus.Errorf("failed to run reboot: %v", err) + return err + } + return nil +} + +func upgradeKubeVersion(req *pb.UpgradeRequest) error { + if isMasterNode() { + if err := upgradeMasterNodes(req.KubeVersion); err != nil { + logrus.Errorf("failed to upgrade master nodes: %v", err) + return err + } + } else { + if err := upgradeWorkerNodes(); err != nil { + logrus.Errorf("failed to upgrade worker nodes: %v", err) + return err + } + } + return nil +} + +func upgradeMasterNodes(version string) error { + if err := exec.Command("/bin/sh", "-c", kubeletUpdateCmd).Run(); err != nil { + logrus.Errorf("failed to restart kubelet: %w", err) + return err + } + args := []string{"-c", upgradeMasterCmd, version} + if err := exec.Command("/bin/sh", args...).Run(); err != nil { + logrus.Errorf("failed to upgrade nodes: %w", err) + return err + } + return nil +} + +func upgradeWorkerNodes() error { + if err := exec.Command("/bin/sh", "-c", kubeletUpdateCmd).Run(); err != nil { + logrus.Errorf("failed to restart kubelet: %w", err) + return err + } + if err := exec.Command("/bin/sh", "-c", upgradeWorkerCmd).Run(); err != nil { + logrus.Errorf("failed to upgrade nodes: %w", err) + return err + } + return nil +} + +func isMasterNode() bool { + return common.IsFileExist(adminFile) +} + +func markNode(file string) error { + if !common.IsFileExist("/var/housekeeper") { + if err := os.MkdirAll("/var/housekeeper", 0644); err != nil { + return err + } + } + args := []string{"-c", "touch", file} + _, err := runCmd("/bin/sh", args...) + if err != nil { + return err + } + return nil +} + +func runCmd(name string, args ...string) ([]byte, error) { + cmd := exec.Command(name, args...) + output, err := cmd.Output() + if err != nil { + logrus.Errorf("error running %s: %s: %w", name, strings.Join(args, " "), err) + return nil, err + } + return output, nil +} diff --git a/housekeeper/go.mod b/housekeeper/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..ed1b7c4a66c61fd2554fc31398f2b5ff487894a7 --- /dev/null +++ b/housekeeper/go.mod @@ -0,0 +1,71 @@ +module housekeeper.io + +go 1.19 + +require ( + github.com/sirupsen/logrus v1.9.0 + google.golang.org/grpc v1.51.0 + google.golang.org/protobuf v1.30.0 + k8s.io/api v0.27.2 + k8s.io/apimachinery v0.27.2 + k8s.io/client-go v0.27.2 + sigs.k8s.io/controller-runtime v0.15.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/zapr v1.2.4 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.1 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/gofuzz v1.1.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.15.1 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.24.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/oauth2 v0.5.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/term v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + golang.org/x/time v0.3.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apiextensions-apiserver v0.27.2 // indirect + k8s.io/component-base v0.27.2 // indirect + k8s.io/klog/v2 v2.90.1 // indirect + k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect + k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/housekeeper/go.sum b/housekeeper/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..ed4a5d617707564063173dd3ce4d4f29aeefc242 --- /dev/null +++ b/housekeeper/go.sum @@ -0,0 +1,319 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= +github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= +github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= +github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc= +gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.27.2 h1:+H17AJpUMvl+clT+BPnKf0E3ksMAzoBBg7CntpSuADo= +k8s.io/api v0.27.2/go.mod h1:ENmbocXfBT2ADujUXcBhHV55RIT31IIEvkntP6vZKS4= +k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo= +k8s.io/apiextensions-apiserver v0.27.2/go.mod h1:Oz9UdvGguL3ULgRdY9QMUzL2RZImotgxvGjdWRq6ZXQ= +k8s.io/apimachinery v0.27.2 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg= +k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= +k8s.io/client-go v0.27.2 h1:vDLSeuYvCHKeoQRhCXjxXO45nHVv2Ip4Fe0MfioMrhE= +k8s.io/client-go v0.27.2/go.mod h1:tY0gVmUsHrAmjzHX9zs7eCjxcBsf8IiNe7KQ52biTcQ= +k8s.io/component-base v0.27.2 h1:neju+7s/r5O4x4/txeUONNTS9r1HsPbyoPBAtHsDCpo= +k8s.io/component-base v0.27.2/go.mod h1:5UPk7EjfgrfgRIuDBFtsEFAe4DAvP3U+M8RTzoSJkpo= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= +k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= +k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU= +sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/housekeeper/operator/PROJECT b/housekeeper/operator/PROJECT new file mode 100644 index 0000000000000000000000000000000000000000..3abbae333d93711755e282de82828bf275c94590 --- /dev/null +++ b/housekeeper/operator/PROJECT @@ -0,0 +1,14 @@ +layout: +- go.kubebuilder.io/v3 +projectName: housekeeper +repo: housekeeper.io +resources: +- api: + crdVersion: v1 + namespaced: true + controller: true + group: housekeeper.io + kind: Update + path: housekeeper.io/api/v1alpha1 + version: v1alpha1 +version: "3" diff --git a/housekeeper/operator/api/v1alpha1/groupversion_info.go b/housekeeper/operator/api/v1alpha1/groupversion_info.go new file mode 100644 index 0000000000000000000000000000000000000000..cd70622496edab3bd6c524634bc01deb58867e52 --- /dev/null +++ b/housekeeper/operator/api/v1alpha1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +Copyright 2023 KylinSoft Co., 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 v1alpha1 contains API Schema definitions for the housekeeper.io v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=housekeeper.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: "housekeeper.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 +) diff --git a/housekeeper/operator/api/v1alpha1/update_types.go b/housekeeper/operator/api/v1alpha1/update_types.go new file mode 100644 index 0000000000000000000000000000000000000000..300e9504320768aba18a174c46d6783462c97f8e --- /dev/null +++ b/housekeeper/operator/api/v1alpha1/update_types.go @@ -0,0 +1,65 @@ +/* +Copyright 2023 KylinSoft Co., 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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// UpdateSpec defines the desired state of Update +type UpdateSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + OSVersion string `json:"osVersion"` + OSImageURL string `json:"osImageURL"` + KubeVersion string `json:"kubeVersion"` + EvictPodForce bool `json:"evictPodForce"` +} + +// UpdateStatus defines the observed state of Update +type UpdateStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// Update is the Schema for the updates API +type Update struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec UpdateSpec `json:"spec,omitempty"` + Status UpdateStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// UpdateList contains a list of Update +type UpdateList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Update `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Update{}, &UpdateList{}) +} diff --git a/housekeeper/operator/api/v1alpha1/zz_generated.deepcopy.go b/housekeeper/operator/api/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000000000000000000000000000000000000..c7c95179599a05889c834e76ed829efe4295b216 --- /dev/null +++ b/housekeeper/operator/api/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,115 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2023 KylinSoft Co., 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. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +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 *Update) DeepCopyInto(out *Update) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Update. +func (in *Update) DeepCopy() *Update { + if in == nil { + return nil + } + out := new(Update) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Update) 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 *UpdateList) DeepCopyInto(out *UpdateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Update, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpdateList. +func (in *UpdateList) DeepCopy() *UpdateList { + if in == nil { + return nil + } + out := new(UpdateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *UpdateList) 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 *UpdateSpec) DeepCopyInto(out *UpdateSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpdateSpec. +func (in *UpdateSpec) DeepCopy() *UpdateSpec { + if in == nil { + return nil + } + out := new(UpdateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpdateStatus) DeepCopyInto(out *UpdateStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpdateStatus. +func (in *UpdateStatus) DeepCopy() *UpdateStatus { + if in == nil { + return nil + } + out := new(UpdateStatus) + in.DeepCopyInto(out) + return out +} diff --git a/housekeeper/operator/config/crd/housekeeper.io_updates.yaml b/housekeeper/operator/config/crd/housekeeper.io_updates.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2938aa04722d3143d32f77bb51dfb473c5197889 --- /dev/null +++ b/housekeeper/operator/config/crd/housekeeper.io_updates.yaml @@ -0,0 +1,63 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: updates.housekeeper.io +spec: + group: housekeeper.io + names: + kind: Update + listKind: UpdateList + plural: updates + singular: update + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Update is the Schema for the updates API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: UpdateSpec defines the desired state of Update + properties: + kubeVersion: + description: 'The version used to upgrade k8s' + type: string + osImageURL: + description: 'The image url used to upgrade OS' + type: string + osVersion: + description: 'The version used to upgrade OS' + type: string + evictPodForce: + description: 'If true, force evict the pod' + type: boolean + required: + - kubeVersion + - osImageURL + - osVersion + - evictPodForce + type: object + status: + description: UpdateStatus defines the observed state of Update + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/housekeeper/operator/config/manager/manager.yaml b/housekeeper/operator/config/manager/manager.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7544d7c0a43ff54f988d63645c970ce1ba664c60 --- /dev/null +++ b/housekeeper/operator/config/manager/manager.yaml @@ -0,0 +1,42 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: housekeeper-system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: housekeeper-operator-manager + namespace: housekeeper-system + labels: + control-plane: housekeeper-operator-manager +spec: + selector: + matchLabels: + control-plane: housekeeper-operator-manager + replicas: 1 + template: + metadata: + labels: + control-plane: housekeeper-operator-manager + spec: + containers: + - command: + - /housekeeper-operator-manager + image: housekeeper-operator:latest + name: housekeeper-operator-manager + securityContext: + allowPrivilegeEscalation: false + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + terminationGracePeriodSeconds: 10 + nodeSelector: + node-role.kubernetes.io/control-plane: "" + tolerations: + - key: "node-role.kubernetes.io/master" + operator: "Exists" diff --git a/housekeeper/operator/config/rbac/role.yaml b/housekeeper/operator/config/rbac/role.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8d34513fb478260d9aee02022c5c1234b372ef59 --- /dev/null +++ b/housekeeper/operator/config/rbac/role.yaml @@ -0,0 +1,63 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: update-manager-role +rules: +- apiGroups: + - housekeeper.io + resources: + - updates + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - housekeeper.io + resources: + - updates/finalizers + verbs: + - update +- apiGroups: + - housekeeper.io + resources: + - updates/status + verbs: + - get + - patch + - update +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods + verbs: + - get + - list +- apiGroups: + - "" + resources: + - pods/eviction + verbs: + - create +- apiGroups: + - apps + resources: + - daemonsets + verbs: + - delete + - get diff --git a/housekeeper/operator/config/rbac/role_binding.yaml b/housekeeper/operator/config/rbac/role_binding.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5ac01f2d87952c6dd5789184bafb5cd2d82b8bee --- /dev/null +++ b/housekeeper/operator/config/rbac/role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: update-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: update-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: housekeeper-system diff --git a/housekeeper/operator/config/samples/housekeeper.io_v1alpha1_update.yaml b/housekeeper/operator/config/samples/housekeeper.io_v1alpha1_update.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b6106bb6392d041a8c8d401e7aa18b388e4f7d6b --- /dev/null +++ b/housekeeper/operator/config/samples/housekeeper.io_v1alpha1_update.yaml @@ -0,0 +1,8 @@ +apiVersion: housekeeper.io/v1alpha1 +kind: Update +metadata: + name: update-sample +spec: + osVersion: os.version + osImageURL: image.url + kubeVersion: kubernetes.version diff --git a/housekeeper/operator/housekeeper-controller/controllers/update_controller.go b/housekeeper/operator/housekeeper-controller/controllers/update_controller.go new file mode 100644 index 0000000000000000000000000000000000000000..ea4e428aa8b33d163afe8e355f7265edd8c0c30a --- /dev/null +++ b/housekeeper/operator/housekeeper-controller/controllers/update_controller.go @@ -0,0 +1,223 @@ +/* +Copyright 2023 KylinSoft Co., 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 controllers + +import ( + "context" + "fmt" + "os" + + "github.com/sirupsen/logrus" + housekeeperiov1alpha1 "housekeeper.io/operator/api/v1alpha1" + "housekeeper.io/pkg/common" + "housekeeper.io/pkg/connection" + "housekeeper.io/pkg/constants" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" + "k8s.io/kubectl/pkg/drain" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +// UpdateReconciler reconciles a Update object +type UpdateReconciler struct { + client.Client + Scheme *runtime.Scheme + KubeClientSet kubernetes.Interface + Connection *connection.Client + HostName string +} + +//+kubebuilder:rbac:groups=housekeeper.io,resources=updates,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=housekeeper.io,resources=updates/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=housekeeper.io,resources=updates/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Update object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.0/pkg/reconcile +func NewUpdateReconciler(mgr manager.Manager) *UpdateReconciler { + kubeClientSet, err := kubernetes.NewForConfig(mgr.GetConfig()) + if err != nil { + logrus.Errorf("failed to build the kubernetes clientset: %v", err) + } + reconciler := &UpdateReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + KubeClientSet: kubeClientSet, + HostName: os.Getenv("NODE_NAME"), + } + return reconciler +} + +func (r *UpdateReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + ctx = context.Background() + upInstance, nodeInstance := reqInstance(ctx, r, req.NamespacedName, r.HostName) + var ( + osVersionSpec = upInstance.Spec.OSVersion + kubeVersionSpec = upInstance.Spec.KubeVersion + // osVersion reported by the node from /etc/os-release + osVersion = nodeInstance.Status.NodeInfo.OSImage + ) + upgradeCluster := checkUpgrade(osVersion, osVersionSpec, kubeVersionSpec) + if upgradeCluster { + if err := r.upgradeNodes(ctx, &upInstance, &nodeInstance); err != nil { + return common.RequeueNow, err + } + } else { + r.refreshNodes(ctx, &upInstance, &nodeInstance) + } + return common.RequeueAfter, nil +} + +func (r *UpdateReconciler) upgradeNodes(ctx context.Context, upInstance *housekeeperiov1alpha1.Update, + node *corev1.Node) error { + if _, ok := node.Labels[constants.LabelUpgrading]; ok { + drainer := &drain.Helper{ + Ctx: ctx, + Client: r.KubeClientSet, + IgnoreAllDaemonSets: true, + DeleteEmptyDirData: true, + GracePeriodSeconds: -1, + Out: os.Stdout, + ErrOut: os.Stderr, + } + if upInstance.Spec.EvictPodForce { + drainer.Force = true + } + if err := drainNode(drainer, node); err != nil { + return err + } + pushInfo := &connection.PushInfo{ + KubeVersion: upInstance.Spec.KubeVersion, + OSImageURL: upInstance.Spec.OSImageURL, + OSVersion: upInstance.Spec.OSVersion, + } + if err := r.Connection.UpgradeKubeSpec(pushInfo); err != nil { + return err + } + } + + return nil +} + +func (r *UpdateReconciler) refreshNodes(ctx context.Context, upInstance *housekeeperiov1alpha1.Update, node *corev1.Node) error { + deleteLabel(ctx, r, node) + if node.Spec.Unschedulable { + drainer := &drain.Helper{ + Ctx: ctx, + Client: r.KubeClientSet, + GracePeriodSeconds: -1, + Out: os.Stdout, + ErrOut: os.Stderr, + } + if err := cordonOrUncordonNode(false, drainer, node); err != nil { + logrus.Errorf("failed to uncordon node %s: %v", node.Name, err) + return err + } + logrus.Infof("uncordon successfully %s node", node.Name) + } + return nil +} + +func deleteLabel(ctx context.Context, r common.ReadWriterClient, node *corev1.Node) error { + if _, ok := node.Labels[constants.LabelUpgrading]; ok { + delete(node.Labels, constants.LabelUpgrading) + if err := r.Update(ctx, node); err != nil { + logrus.Errorf("unable to delete %s node label: %w", node.Name, err) + return err + } + } + return nil +} + +// Sets schedulable or not +func cordonOrUncordonNode(desired bool, drainer *drain.Helper, node *corev1.Node) error { + carry := "cordon" + if !desired { + carry = "uncordon" + } + logrus.Info(node.Name, "initiating %s", carry) + if node.Spec.Unschedulable == desired { + return nil + } + err := drain.RunCordonOrUncordon(drainer, node, desired) + if err != nil { + return fmt.Errorf("failed to %s: %w", carry, err) + } + return nil +} + +func drainNode(drainer *drain.Helper, node *corev1.Node) error { + logrus.Info(node.Name, "is cordoning") + // Perform cordon + if err := cordonOrUncordonNode(true, drainer, node); err != nil { + return fmt.Errorf("failed to cordon node %s: %v", node.Name, err) + } + // Attempt drain + logrus.Info(node.Name, "initiating drain") + if err := drain.RunNodeDrain(drainer, node.Name); err != nil { + return fmt.Errorf("unable to drain: %v", err) + } + return nil +} + +func reqInstance(ctx context.Context, r common.ReadWriterClient, name types.NamespacedName, + HostName string) (upInstance housekeeperiov1alpha1.Update, nodeInstance corev1.Node) { + if err := r.Get(ctx, name, &upInstance); err != nil { + logrus.Errorf("unable to fetch update instance: %v", err) + return + } + if err := r.Get(ctx, client.ObjectKey{Name: HostName}, &nodeInstance); err != nil { + logrus.Errorf("unable to fetch node instance: %v", err) + return + } + return +} + +// Check if the version is upgraded +func checkUpgrade(osVersion string, osVersionSpec string, kubeVersionSpec string) bool { + if len(kubeVersionSpec) > 0 { + markFile := fmt.Sprintf("%s%s%s", "/var/housekeeper/", kubeVersionSpec, ".stamp") + if common.IsFileExist(markFile) { + return false + } + } else { + return osVersion != osVersionSpec + } + + return true +} + +// SetupWithManager sets up the controller with the Manager. +func (r *UpdateReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&housekeeperiov1alpha1.Update{}). + Complete(r) +} diff --git a/housekeeper/operator/housekeeper-controller/main.go b/housekeeper/operator/housekeeper-controller/main.go new file mode 100644 index 0000000000000000000000000000000000000000..dec60a6ee27c78a6ac8aac56b94c673ba564668c --- /dev/null +++ b/housekeeper/operator/housekeeper-controller/main.go @@ -0,0 +1,83 @@ +/* +Copyright 2023 KylinSoft Co., 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 main + +import ( + "flag" + "os" + "path/filepath" + + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. + "github.com/sirupsen/logrus" + _ "k8s.io/client-go/plugin/pkg/client/auth" + + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + housekeeperiov1alpha1 "housekeeper.io/operator/api/v1alpha1" + "housekeeper.io/operator/housekeeper-controller/controllers" + "housekeeper.io/pkg/connection" + "housekeeper.io/pkg/constants" + "housekeeper.io/pkg/version" + //+kubebuilder:scaffold:imports +) + +var scheme = runtime.NewScheme() + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + + utilruntime.Must(housekeeperiov1alpha1.AddToScheme(scheme)) + //+kubebuilder:scaffold:scheme +} + +func main() { + var err error + opts := zap.Options{} + opts.BindFlags(flag.CommandLine) + flag.Parse() + + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + HealthProbeBindAddress: "0", + }) + if err != nil { + logrus.Errorf("unable to start manager: %v", err) + os.Exit(1) + } + + reconciler := controllers.NewUpdateReconciler(mgr) + if reconciler.Connection, err = connection.New("unix://" + filepath.Join(constants.SockDir, constants.SockName)); err != nil { + logrus.Errorf("unable running housekeeper-controller: %v", err) + } + if err = reconciler.SetupWithManager(mgr); err != nil { + logrus.Error(err, "unable to create controller", "controller", "Update") + os.Exit(1) + } + + logrus.Info("starting housekeeper-controller manager version:", version.Version) + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + logrus.Errorf("problem running housekeeper-controller manager: %v", err) + os.Exit(1) + } +} diff --git a/housekeeper/operator/housekeeper-operator/controllers/update_controller.go b/housekeeper/operator/housekeeper-operator/controllers/update_controller.go new file mode 100644 index 0000000000000000000000000000000000000000..266bc612cd56edc2bc2e91b8794429b4dc6c4a6e --- /dev/null +++ b/housekeeper/operator/housekeeper-operator/controllers/update_controller.go @@ -0,0 +1,246 @@ +/* +Copyright 2023 KylinSoft Co., 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 controllers + +import ( + "context" + "sync" + "time" + + "github.com/sirupsen/logrus" + housekeeperiov1alpha1 "housekeeper.io/operator/api/v1alpha1" + "housekeeper.io/pkg/common" + "housekeeper.io/pkg/constants" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/selection" + "k8s.io/apimachinery/pkg/util/wait" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +// UpdateReconciler reconciles a Update object +type UpdateReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +//+kubebuilder:rbac:groups=housekeeper.io,resources=updates,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=housekeeper.io,resources=updates/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=housekeeper.io,resources=updates/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Update object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.0/pkg/reconcile +func (r *UpdateReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + if r.Client == nil { + return common.NoRequeue, nil + } + var crMutex sync.Mutex + crMutex.Lock() + defer crMutex.Unlock() + ctx = context.Background() + return reconcile(ctx, r, req) +} + +// SetupWithManager sets up the controller with the Manager. +func (r *UpdateReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&housekeeperiov1alpha1.Update{}). + Complete(r) +} + +func reconcile(ctx context.Context, r common.ReadWriterClient, req ctrl.Request) (ctrl.Result, error) { + var update housekeeperiov1alpha1.Update + if err := r.Get(ctx, req.NamespacedName, &update); err != nil { + logrus.Errorf("unable to fetch update instance: %v", err) + return common.NoRequeue, err + } + if len(update.Spec.OSVersion) == 0 { + logrus.Warning("os version is required") + return common.RequeueAfter, nil + } + masterNodesItems, err := getMasterNodesItems(ctx, r) + if err != nil { + return common.RequeueNow, err + } + workerNodesItems, err := getWorkerNodesItems(ctx, r) + if err != nil { + return common.RequeueNow, err + } + if assignUpdated(ctx, r, masterNodesItems, 1, update); err != nil { + return common.RequeueNow, err + } + maxUnavailable := min(update.Spec.MaxUnavailable, len(workerNodesItems)) + if assignUpdated(ctx, r, masterNodesItems, maxUnavailable, update); err != nil { + return common.RequeueNow, err + } + + return common.NoRequeue, nil +} + +func getMasterNodesItems(ctx context.Context, r common.ReadWriterClient) ( + nodesItems []corev1.Node, err error) { + reqUpgrade, err := labels.NewRequirement(constants.LabelUpgrading, selection.DoesNotExist, nil) + if err != nil { + logrus.Errorf("unable to create requirement %s: %v", reqUpgrade, err) + return + } + reqMaster, err := labels.NewRequirement(constants.LabelMaster, selection.Exists, nil) + if err != nil { + logrus.Errorf("unable to create requirement %s: %v", constants.LabelMaster, err) + return + } + nodesItems, err = getNodes(ctx, r, *reqUpgrade, *reqMaster) + if err != nil { + logrus.Errorf("failed to get master nodes list: %v", err) + return + } + return +} + +func getWorkerNodesItems(ctx context.Context, r common.ReadWriterClient) ( + nodesItems []corev1.Node, err error) { + reqUpgrade, err := labels.NewRequirement(constants.LabelUpgrading, selection.DoesNotExist, nil) + if err != nil { + logrus.Errorf("unable to create requirement %s: %v", reqUpgrade, err) + return + } + reqWorker, err := labels.NewRequirement(constants.LabelMaster, selection.DoesNotExist, nil) + if err != nil { + logrus.Errorf("unable to create requirement %s: %v"+constants.LabelMaster, err) + return + } + nodesItems, err = getNodes(ctx, r, *reqUpgrade, *reqWorker) + if err != nil { + logrus.Errorf("failed to get worker nodes list: %v", err) + return + } + return +} + +func getNodes(ctx context.Context, r common.ReadWriterClient, reqs ...labels.Requirement) ([]corev1.Node, error) { + var nodeList corev1.NodeList + opts := client.ListOptions{LabelSelector: labels.NewSelector().Add(reqs...)} + if err := r.List(ctx, &nodeList, &opts); err != nil { + logrus.Errorf("unable to list nodes with requirements: %v", err) + return nil, err + } + return nodeList.Items, nil +} + +// Add the label to nodes +func assignUpdated(ctx context.Context, r common.ReadWriterClient, nodeList []corev1.Node, + maxUnavailable int, upInstance housekeeperiov1alpha1.Update) error { + var ( + kubeVersionSpec = upInstance.Spec.KubeVersion + osVersionSpec = upInstance.Spec.OSVersion + count = 0 + wg sync.WaitGroup + ) + + // 创建一个通道来接收任务结果 + resultChan := make(chan error) + + for _, node := range nodeList { + if count >= maxUnavailable { + count = 0 + //为了控制升级任务的并发数,每处理 maxUnavailable 个节点后,休眠 2 分钟 + time.Sleep(constants.NodeSleepTime) + } + if conditionMet(node, kubeVersionSpec, osVersionSpec) { + node.Labels[constants.LabelUpgrading] = "" + if err := r.Update(ctx, &node); err != nil { + logrus.Errorf("unable to add %s label:%v", node.Name, err) + return err + } + count++ + wg.Add(1) // 增加 WaitGroup 的计数器 + go func(node corev1.Node) { + waitForUpgradeComplete(node, kubeVersionSpec, osVersionSpec, resultChan, &wg) + }(node) + } + } + //等待所有任务完成 + wg.Wait() + + //关闭结果通道 + close(resultChan) + // 遍历结果通道,处理每个任务的结果 + for err := range resultChan { + if err != nil { + return err + } + } + return nil +} + +func waitForUpgradeComplete(node corev1.Node, kubeVersionSpec string, osVersionSpec string, + resultChan chan<- error, wg *sync.WaitGroup) { + defer wg.Done() // goroutine 执行完成后减少 WaitGroup 的计数器 + + ctx, cancel := context.WithTimeout(context.Background(), constants.NodeTimeout) + defer cancel() + done := make(chan struct{}) + + go func() { + wait.Until(func() { + if !conditionMet(node, kubeVersionSpec, osVersionSpec) { + close(done) + } + }, 10*time.Second, ctx.Done()) + }() + + select { + case <-done: + logrus.Infof("successful upgrade node: %s", node.Name) + resultChan <- nil + case <-ctx.Done(): + // 上下文超时,跳出循环 + if ctx.Err() == context.DeadlineExceeded { + logrus.Errorf("failed to upgrade node: %s: %v", node.Name, ctx.Err()) + resultChan <- ctx.Err() + } + } + //确保在任务完成后关闭done通道 + close(done) +} + +func conditionMet(node corev1.Node, kubeVersionSpec string, osVersionSpec string) bool { + nodeInfo := node.Status.NodeInfo + if kubeVersionSpec != "" { + return kubeVersionSpec != nodeInfo.KubeProxyVersion && kubeVersionSpec != nodeInfo.KubeletVersion + } + return osVersionSpec != nodeInfo.OSImage +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/housekeeper/operator/housekeeper-operator/main.go b/housekeeper/operator/housekeeper-operator/main.go new file mode 100644 index 0000000000000000000000000000000000000000..f1f66e17c487bece5d7b1b0e24cc47864c4f7d0c --- /dev/null +++ b/housekeeper/operator/housekeeper-operator/main.go @@ -0,0 +1,89 @@ +/* +Copyright 2023 KylinSoft Co., 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 main + +import ( + "flag" + "os" + + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. + + "github.com/sirupsen/logrus" + _ "k8s.io/client-go/plugin/pkg/client/auth" + + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + housekeeperiov1alpha1 "housekeeper.io/operator/api/v1alpha1" + "housekeeper.io/operator/housekeeper-operator/controllers" + "housekeeper.io/pkg/version" + //+kubebuilder:scaffold:imports +) + +var scheme = runtime.NewScheme() + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + + utilruntime.Must(housekeeperiov1alpha1.AddToScheme(scheme)) + //+kubebuilder:scaffold:scheme +} + +func main() { + opts := zap.Options{} + opts.BindFlags(flag.CommandLine) + flag.Parse() + + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + HealthProbeBindAddress: "0", + }) + if err != nil { + logrus.Error(err, "unable to start manager") + os.Exit(1) + } + + if err = (&controllers.UpdateReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + logrus.Error(err, "unable to create controller", "controller", "Update") + os.Exit(1) + } + //+kubebuilder:scaffold:builder + + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + logrus.Errorf("unable to set up health check: %v", err) + os.Exit(1) + } + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + logrus.Errorf("unable to set up ready check: %v", err) + os.Exit(1) + } + logrus.Infof("starting housekeeper-operator manager version:", version.Version) + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + logrus.Errorf("problem running housekeeper-operator manager: %v", err) + os.Exit(1) + } +} diff --git a/housekeeper/pkg/common/common.go b/housekeeper/pkg/common/common.go new file mode 100644 index 0000000000000000000000000000000000000000..bac1b785c793c6715212c19599adda9163cf92cc --- /dev/null +++ b/housekeeper/pkg/common/common.go @@ -0,0 +1,50 @@ +/* +Copyright 2023 KylinSoft Co., 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 common + +import ( + "os" + "time" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// ReadWriterClient is Kubernetes API +type ReadWriterClient interface { + client.Reader + client.StatusClient + client.Writer +} + +var ( + // controller do not requeue + NoRequeue = ctrl.Result{} + // controller requeue + RequeueNow = ctrl.Result{Requeue: true} + RequeueAfter = ctrl.Result{Requeue: true, RequeueAfter: time.Second * 20} +) + +func IsFileExist(path string) bool { + fileInfo, err := os.Stat(path) + if err != nil { + return false + } + if fileInfo.IsDir() { + return false + } + return true +} diff --git a/housekeeper/pkg/connection/connection.go b/housekeeper/pkg/connection/connection.go new file mode 100644 index 0000000000000000000000000000000000000000..546e977f89b0a5753b824e990a463f4a724f7ec5 --- /dev/null +++ b/housekeeper/pkg/connection/connection.go @@ -0,0 +1,65 @@ +/* +Copyright 2023 KylinSoft Co., 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. +*/ +// connection between client and server +package connection + +import ( + "context" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/backoff" + + pb "housekeeper.io/pkg/connection/proto" +) + +type Client struct { + socketAddress string + client pb.UpgradeClusterClient +} + +type PushInfo struct { + OSImageURL string + OSVersion string + KubeVersion string +} + +// Create a grpc channel +func New(socketAddr string) (*Client, error) { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + bc := backoff.DefaultConfig + bc.MaxDelay = 5 * time.Second + + connection, err := grpc.DialContext(ctx, socketAddr, grpc.WithInsecure(), grpc.WithBlock(), + grpc.WithConnectParams(grpc.ConnectParams{Backoff: bc})) + if err != nil { + return nil, err + } + return &Client{socketAddress: socketAddr, client: pb.NewUpgradeClusterClient(connection)}, nil +} + +// send update requests +func (c *Client) UpgradeKubeSpec(pushInfo *PushInfo) error { + _, err := c.client.Upgrade(context.Background(), + &pb.UpgradeRequest{ + KubeVersion: pushInfo.KubeVersion, + OsImageUrl: pushInfo.OSImageURL, + OsVersion: pushInfo.OSVersion, + }) + return err +} diff --git a/housekeeper/pkg/connection/proto/Makefile b/housekeeper/pkg/connection/proto/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..7e8277ab6b5ef0f9e6154c76dc5f83ec07c23e26 --- /dev/null +++ b/housekeeper/pkg/connection/proto/Makefile @@ -0,0 +1,17 @@ +# Copyright 2023 KylinSoft Co., 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. + + +grpc: + protoc --go_out=plugins=grpc:. --go_opt=paths=source_relative daemon.proto diff --git a/housekeeper/pkg/connection/proto/daemon.pb.go b/housekeeper/pkg/connection/proto/daemon.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..0446d5b4cdfd7951517708e1a15258e1fa4843b9 --- /dev/null +++ b/housekeeper/pkg/connection/proto/daemon.pb.go @@ -0,0 +1,332 @@ +// +//Copyright 2023 KylinSoft Co., 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.20.3 +// source: daemon.proto + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type UpgradeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + KubeVersion string `protobuf:"bytes,1,opt,name=kube_version,json=kubeVersion,proto3" json:"kube_version,omitempty"` + OsImageUrl string `protobuf:"bytes,2,opt,name=os_image_url,json=osImageUrl,proto3" json:"os_image_url,omitempty"` + OsVersion string `protobuf:"bytes,3,opt,name=os_version,json=osVersion,proto3" json:"os_version,omitempty"` +} + +func (x *UpgradeRequest) Reset() { + *x = UpgradeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_daemon_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpgradeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpgradeRequest) ProtoMessage() {} + +func (x *UpgradeRequest) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpgradeRequest.ProtoReflect.Descriptor instead. +func (*UpgradeRequest) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{0} +} + +func (x *UpgradeRequest) GetKubeVersion() string { + if x != nil { + return x.KubeVersion + } + return "" +} + +func (x *UpgradeRequest) GetOsImageUrl() string { + if x != nil { + return x.OsImageUrl + } + return "" +} + +func (x *UpgradeRequest) GetOsVersion() string { + if x != nil { + return x.OsVersion + } + return "" +} + +type UpgradeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Err int32 `protobuf:"varint,1,opt,name=err,proto3" json:"err,omitempty"` +} + +func (x *UpgradeResponse) Reset() { + *x = UpgradeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_daemon_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpgradeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpgradeResponse) ProtoMessage() {} + +func (x *UpgradeResponse) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpgradeResponse.ProtoReflect.Descriptor instead. +func (*UpgradeResponse) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{1} +} + +func (x *UpgradeResponse) GetErr() int32 { + if x != nil { + return x.Err + } + return 0 +} + +var File_daemon_proto protoreflect.FileDescriptor + +var file_daemon_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x22, 0x74, 0x0a, 0x0e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6b, 0x75, 0x62, 0x65, + 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x6b, 0x75, 0x62, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0c, 0x6f, + 0x73, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x6f, 0x73, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x1d, 0x0a, + 0x0a, 0x6f, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x23, 0x0a, 0x0f, + 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x65, 0x72, + 0x72, 0x32, 0x4e, 0x0a, 0x0e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x07, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x12, 0x16, + 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, + 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x42, 0x25, 0x5a, 0x23, 0x68, 0x6f, 0x75, 0x73, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x65, 0x72, + 0x2e, 0x69, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_daemon_proto_rawDescOnce sync.Once + file_daemon_proto_rawDescData = file_daemon_proto_rawDesc +) + +func file_daemon_proto_rawDescGZIP() []byte { + file_daemon_proto_rawDescOnce.Do(func() { + file_daemon_proto_rawDescData = protoimpl.X.CompressGZIP(file_daemon_proto_rawDescData) + }) + return file_daemon_proto_rawDescData +} + +var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_daemon_proto_goTypes = []interface{}{ + (*UpgradeRequest)(nil), // 0: daemon.UpgradeRequest + (*UpgradeResponse)(nil), // 1: daemon.UpgradeResponse +} +var file_daemon_proto_depIdxs = []int32{ + 0, // 0: daemon.UpgradeCluster.Upgrade:input_type -> daemon.UpgradeRequest + 1, // 1: daemon.UpgradeCluster.Upgrade:output_type -> daemon.UpgradeResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_daemon_proto_init() } +func file_daemon_proto_init() { + if File_daemon_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_daemon_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpgradeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_daemon_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpgradeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_daemon_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_daemon_proto_goTypes, + DependencyIndexes: file_daemon_proto_depIdxs, + MessageInfos: file_daemon_proto_msgTypes, + }.Build() + File_daemon_proto = out.File + file_daemon_proto_rawDesc = nil + file_daemon_proto_goTypes = nil + file_daemon_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// UpgradeClusterClient is the client API for UpgradeCluster service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type UpgradeClusterClient interface { + Upgrade(ctx context.Context, in *UpgradeRequest, opts ...grpc.CallOption) (*UpgradeResponse, error) +} + +type upgradeClusterClient struct { + cc grpc.ClientConnInterface +} + +func NewUpgradeClusterClient(cc grpc.ClientConnInterface) UpgradeClusterClient { + return &upgradeClusterClient{cc} +} + +func (c *upgradeClusterClient) Upgrade(ctx context.Context, in *UpgradeRequest, opts ...grpc.CallOption) (*UpgradeResponse, error) { + out := new(UpgradeResponse) + err := c.cc.Invoke(ctx, "/daemon.UpgradeCluster/Upgrade", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// UpgradeClusterServer is the server API for UpgradeCluster service. +type UpgradeClusterServer interface { + Upgrade(context.Context, *UpgradeRequest) (*UpgradeResponse, error) +} + +// UnimplementedUpgradeClusterServer can be embedded to have forward compatible implementations. +type UnimplementedUpgradeClusterServer struct { +} + +func (*UnimplementedUpgradeClusterServer) Upgrade(context.Context, *UpgradeRequest) (*UpgradeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Upgrade not implemented") +} + +func RegisterUpgradeClusterServer(s *grpc.Server, srv UpgradeClusterServer) { + s.RegisterService(&_UpgradeCluster_serviceDesc, srv) +} + +func _UpgradeCluster_Upgrade_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpgradeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UpgradeClusterServer).Upgrade(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/daemon.UpgradeCluster/Upgrade", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UpgradeClusterServer).Upgrade(ctx, req.(*UpgradeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _UpgradeCluster_serviceDesc = grpc.ServiceDesc{ + ServiceName: "daemon.UpgradeCluster", + HandlerType: (*UpgradeClusterServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Upgrade", + Handler: _UpgradeCluster_Upgrade_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "daemon.proto", +} diff --git a/housekeeper/pkg/connection/proto/daemon.proto b/housekeeper/pkg/connection/proto/daemon.proto new file mode 100644 index 0000000000000000000000000000000000000000..f4f5c44b2525a89d1bf1dcb68b7c545a06f250b5 --- /dev/null +++ b/housekeeper/pkg/connection/proto/daemon.proto @@ -0,0 +1,35 @@ +/* +Copyright 2023 KylinSoft Co., 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. +*/ + +syntax = "proto3"; + +option go_package = "housekeeper.io/pkg/connection/proto"; + +package daemon; + +service UpgradeCluster{ + rpc Upgrade(UpgradeRequest) returns (UpgradeResponse) {} +} + +message UpgradeRequest { + string kube_version = 1; + string os_image_url = 2; + string os_version = 3; +} + +message UpgradeResponse { + int32 err = 1; +} \ No newline at end of file diff --git a/housekeeper/pkg/constants/constants.go b/housekeeper/pkg/constants/constants.go new file mode 100644 index 0000000000000000000000000000000000000000..05c167e4de88fa142d26e389525ace51bcd91409 --- /dev/null +++ b/housekeeper/pkg/constants/constants.go @@ -0,0 +1,38 @@ +/* +Copyright 2023 KylinSoft Co., 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 constants + +import "time" + +const ( + // LabelUpgrading is the key of the upgrading label for nodes + LabelUpgrading = "upgrade.housekeeper.io/upgrading" + // LabelMaster defines the label associated with master node. + LabelMaster = "node-role.kubernetes.io/master" +) + +// socket file +const ( + SockDir = "/run/housekeeper-daemon" + SockName = "housekeeper-daemon.sock" +) + +const ( + // node upgrade timeout + NodeTimeout = 5 * time.Minute + // time to sleep after processing maxUnavailable nodes + NodeSleepTime = 2 * time.Minute +) diff --git a/housekeeper/pkg/version/version.go b/housekeeper/pkg/version/version.go new file mode 100644 index 0000000000000000000000000000000000000000..737ec7bf49ae866ca66e086ee4c367f68840aab9 --- /dev/null +++ b/housekeeper/pkg/version/version.go @@ -0,0 +1,19 @@ +/* +Copyright 2023 KylinSoft Co., 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 version + +// the version +var Version string = "1.0.0" diff --git a/pkg/apimonitor.go b/pkg/apimonitor.go new file mode 100755 index 0000000000000000000000000000000000000000..39d58243cf3d6e477b81f7f0a966b85143378cab --- /dev/null +++ b/pkg/apimonitor.go @@ -0,0 +1,19 @@ +package pkg + +import "time" + +type ApiMonitor struct { + Endpoint string +} + +func (l ApiMonitor) WaitForClusterReady(timeout time.Duration) { + return +} + +func (l ApiMonitor) WaitForMastersReady(timeout time.Duration){ +} + +func (l ApiMonitor) WaitForWorkersReady(timeout time.Duration){ + return +} + diff --git a/pkg/bootconfig/ignitioncreator.go b/pkg/bootconfig/ignitioncreator.go new file mode 100755 index 0000000000000000000000000000000000000000..25a8517076c538e77df47537e8dc66a4df22af4f --- /dev/null +++ b/pkg/bootconfig/ignitioncreator.go @@ -0,0 +1,10 @@ +package bootconfig + +import "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra" + +type IgnitionAssembler struct { +} + +func (i IgnitionAssembler) Assemble(assets infra.Assets) infra.InitConfig { + return infra.InitConfig{} +} diff --git a/pkg/bootconfig/initconfig.go b/pkg/bootconfig/initconfig.go new file mode 100755 index 0000000000000000000000000000000000000000..b1be118c0a000a736a5b3da78af665f1a693dc45 --- /dev/null +++ b/pkg/bootconfig/initconfig.go @@ -0,0 +1,3 @@ +package bootconfig + + diff --git a/pkg/cert/cacert.go b/pkg/cert/cacert.go new file mode 100644 index 0000000000000000000000000000000000000000..0e1227ec01fe78f18f64d8ef22bccad3c2f8ea85 --- /dev/null +++ b/pkg/cert/cacert.go @@ -0,0 +1,37 @@ +/* +Copyright 2023 KylinSoft Co., 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 cert + +import ( + "crypto/x509" + "crypto/x509/pkix" +) + +type RootCA struct { + SelfSignedCertKey +} + +func (c *RootCA) Generate() error { + cfg := &CertConfig{ + Subject: pkix.Name{CommonName: "rootca", OrganizationalUnit: []string{"NestOS"}}, + KeyUsages: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + Validity: 3650, + IsCA: true, + } + + return c.SelfSignedCertKey.Generate(cfg, "rootca") +} diff --git a/pkg/cert/certapi.go b/pkg/cert/certapi.go new file mode 100644 index 0000000000000000000000000000000000000000..4c7c642f8e166e675431640f419f4f8041b1f03c --- /dev/null +++ b/pkg/cert/certapi.go @@ -0,0 +1,58 @@ +/* +Copyright 2023 KylinSoft Co., 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 cert + +import ( + "crypto/x509" + "crypto/x509/pkix" + "net" + "time" +) + +type CertInterface interface { + // Cert returns the certificate. + Cert() []byte +} + +// CertKeyInterface contains a private key and the associated cert. +type CertKeyInterface interface { + CertInterface + // Key returns the private key. + Key() []byte +} + +type CertificateGenerator interface { + GenerateCACertificate() error + GenerateSignedCertificate(commonName string) error +} + +// CertKey 包含证书和私钥 +type CertKey struct { + CertRaw []byte + KeyRaw []byte + SavePath string +} + +type CertConfig struct { + DNSNames []string + ExtKeyUsages []x509.ExtKeyUsage + IPAddresses []net.IP + KeyUsages x509.KeyUsage + Subject pkix.Name + Validity time.Duration + IsCA bool +} diff --git a/pkg/cert/etcd.go b/pkg/cert/etcd.go new file mode 100755 index 0000000000000000000000000000000000000000..57a64f55ac2ca502e1e3423fddcda15f4a1a482c --- /dev/null +++ b/pkg/cert/etcd.go @@ -0,0 +1,31 @@ +/* +Copyright 2023 KylinSoft Co., 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 cert + +import ( + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra" +) + +type EtcdCaGenerator struct { +} + +func (e EtcdCaGenerator) GenerateAssets() infra.Assets { + return nil +} + +func init() { +} diff --git a/pkg/cert/selfsignedcert.go b/pkg/cert/selfsignedcert.go new file mode 100644 index 0000000000000000000000000000000000000000..91f9d4c3323f4fe576cf368724a1a3e50a326c77 --- /dev/null +++ b/pkg/cert/selfsignedcert.go @@ -0,0 +1,101 @@ +/* +Copyright 2023 KylinSoft Co., 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 cert + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "math/big" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// SelfSignedCertificate 只负责创建自签名的证书,这里只传入cfg和privatekey +func SelfSignedCertificate(cfg *CertConfig, key *rsa.PrivateKey) (*x509.Certificate, error) { + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + logrus.Errorf("Failed to generate serialNumber: %v", err) + return nil, err + } + certTemplate := x509.Certificate{ + BasicConstraintsValid: true, + IsCA: cfg.IsCA, + KeyUsage: cfg.KeyUsages, + NotAfter: time.Now().Add(cfg.Validity), + NotBefore: time.Now(), + SerialNumber: serialNumber, + Subject: cfg.Subject, + } + // 判断subject字段中CommonName和OrganizationalUnit是否为空 + if len(cfg.Subject.CommonName) == 0 || len(cfg.Subject.OrganizationalUnit) == 0 { + return nil, errors.Errorf("certification's subject is not set, or invalid") + } + + //certBytes是生成证书的中间步骤,它用于将证书的二进制表示存储在内存中,以便后续操作可以使用它 + certBytes, err := x509.CreateCertificate(rand.Reader, &certTemplate, &certTemplate, key.Public(), key) + if err != nil { + return nil, errors.Wrap(err, "failed to create certificate") + } + + return x509.ParseCertificate(certBytes) +} + +/*GenerateSelfSignedCertificate负责根据cfg生成私钥和证书 + 在这一步会调用PrivateKey生成私钥并调用SelfSignedCertificate生成证书,并将结果返回*/ +func GenerateSelfSignedCertificate(cfg *CertConfig) (*rsa.PrivateKey, *x509.Certificate, error) { + key, err := PrivateKey() + if err != nil { + logrus.Debugf("Failed to generate private key: %s", err) + return nil, nil, errors.Wrap(err, "Failed to generate private key") + } + + //这里的crt是parse之后的,表示已经生成的CA证书,用于存储CA证书的详细信息,例如证书序列号、主题、有效期等 + crt, err := SelfSignedCertificate(cfg, key) + if err != nil { + logrus.Debugf("Failed to create self-signed certificate: %s", err) + return nil, nil, errors.Wrap(err, "failed to create self-signed certificate") + } + return key, crt, nil +} + +type SelfSignedCertKey struct { + CertKey +} + +//自签名证书生成器,封装后该方法用于所有自签名的证书,并将证书和私钥转换格式后保存 +func (c *SelfSignedCertKey) Generate(cfg *CertConfig, filename string) error { + + key, crt, err := GenerateSelfSignedCertificate(cfg) + if err != nil { + return errors.Wrap(err, "Failed to generate self-signed cert/key pair") + } + + c.KeyRaw = PrivateKeyToPem(key) + c.CertRaw = CertToPem(crt) + + err = c.SaveCertificateToFile(filename) + if err != nil { + logrus.Errorf("Faile to save %s: %v", filename, err) + } + + return nil + +} diff --git a/pkg/cert/signedcerts.go b/pkg/cert/signedcerts.go new file mode 100644 index 0000000000000000000000000000000000000000..66d8a4482f64f6aa3b2a874a75b1e902f1ca63b4 --- /dev/null +++ b/pkg/cert/signedcerts.go @@ -0,0 +1,130 @@ +/* +Copyright 2023 KylinSoft Co., 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 cert + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "math/big" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func SignedCertificate( + cfg *CertConfig, + csr *x509.CertificateRequest, + key *rsa.PrivateKey, + caCert *x509.Certificate, + caKey *rsa.PrivateKey, +) (*x509.Certificate, error) { + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + logrus.Errorf("Failed to generate serialNumber: %v", err) + return nil, err + } + certTemplate := x509.Certificate{ + BasicConstraintsValid: true, + IsCA: cfg.IsCA, + DNSNames: csr.DNSNames, + ExtKeyUsage: cfg.ExtKeyUsages, + IPAddresses: csr.IPAddresses, + KeyUsage: cfg.KeyUsages, + NotAfter: time.Now().Add(cfg.Validity), + NotBefore: caCert.NotBefore, + SerialNumber: serialNumber, + Subject: csr.Subject, + } + certBytes, err := x509.CreateCertificate(rand.Reader, &certTemplate, caCert, key.Public(), caKey) + if err != nil { + return nil, errors.Wrap(err, "failed to create x509 certificate") + } + return x509.ParseCertificate(certBytes) +} + +func GenerateSignedCertificate(caKey *rsa.PrivateKey, caCert *x509.Certificate, + cfg *CertConfig) (*rsa.PrivateKey, *x509.Certificate, error) { + key, err := PrivateKey() + if err != nil { + return nil, nil, errors.Wrap(err, "failed to generate private key") + } + // create a CSR + csrTmpl := x509.CertificateRequest{Subject: cfg.Subject, DNSNames: cfg.DNSNames, IPAddresses: cfg.IPAddresses} + csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &csrTmpl, key) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to create certificate request") + } + csr, err := x509.ParseCertificateRequest(csrBytes) + if err != nil { + logrus.Debugf("Failed to parse x509 certificate request: %s", err) + return nil, nil, errors.Wrap(err, "error parsing x509 certificate request") + } + + // create a cert + cert, err := SignedCertificate(cfg, csr, key, caCert, caKey) + if err != nil { + logrus.Debugf("Failed to create a signed certificate: %s", err) + return nil, nil, errors.Wrap(err, "failed to create a signed certificate") + } + return key, cert, nil + +} + +type SignedCertKey struct { + CertKey +} + +func (c *SignedCertKey) Generate( + cfg *CertConfig, + parentCA CertKeyInterface, + filename string, +) error { + var key *rsa.PrivateKey + var crt *x509.Certificate + var err error + + caKey, err := PemToPrivateKey(parentCA.Key()) + if err != nil { + logrus.Debugf("Failed to parse RSA private key: %s", err) + return errors.Wrap(err, "failed to parse rsa private key") + } + + caCert, err := PemToCertificate(parentCA.Cert()) + if err != nil { + logrus.Debugf("Failed to parse x509 certificate: %s", err) + return errors.Wrap(err, "failed to parse x509 certificate") + } + + key, crt, err = GenerateSignedCertificate(caKey, caCert, cfg) + if err != nil { + logrus.Debugf("Failed to generate signed cert/key pair: %s", err) + return errors.Wrap(err, "failed to generate signed cert/key pair") + } + + c.KeyRaw = PrivateKeyToPem(key) + c.CertRaw = CertToPem(crt) + + err = c.SaveCertificateToFile(filename) + if err != nil { + logrus.Errorf("Faile to save %s: %v", filename, err) + } + + return nil +} diff --git a/pkg/cert/tools.go b/pkg/cert/tools.go new file mode 100644 index 0000000000000000000000000000000000000000..4d10d39b52edad1d5e7237e43eda1b2a12c3b235 --- /dev/null +++ b/pkg/cert/tools.go @@ -0,0 +1,103 @@ +/* +Copyright 2023 KylinSoft Co., 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 cert + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "io/ioutil" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +//PrivateKey负责生成密钥 +func PrivateKey() (*rsa.PrivateKey, error) { + rsaKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, errors.Wrap(err, "Failed to generate RSA private key") + } + + return rsaKey, nil +} + +// PrivateKeyToPem 返回私钥的PEM格式字节切片 +func PrivateKeyToPem(key *rsa.PrivateKey) []byte { + keyInBytes := x509.MarshalPKCS1PrivateKey(key) + keyinPem := pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: keyInBytes, + }, + ) + return keyinPem +} + +func PemToPrivateKey(data []byte) (*rsa.PrivateKey, error) { + block, _ := pem.Decode(data) + if block == nil { + return nil, errors.Errorf("could not find a PEM block in the private key") + } + return x509.ParsePKCS1PrivateKey(block.Bytes) +} + +// CACertPEM 返回证书的PEM格式字节切片 +func CertToPem(cert *x509.Certificate) []byte { + certInPem := pem.EncodeToMemory( + &pem.Block{ + Type: "CERTIFICATE", + Bytes: cert.Raw, + }, + ) + return certInPem +} + +func PemToCertificate(data []byte) (*x509.Certificate, error) { + block, _ := pem.Decode(data) + if block == nil { + return nil, errors.Errorf("could not find a PEM block in the certificate") + } + return x509.ParseCertificate(block.Bytes) +} + +// SaveCertificateToFile 将证书保存到文件 +func (c *CertKey) SaveCertificateToFile(filename string) error { + err := ioutil.WriteFile(c.SavePath+"/"+filename, c.CertRaw, 0644) + if err != nil { + logrus.Errorf("Faile to save %s: %v", filename, err) + return err + } + + logrus.Infof("Successfully saved %s", filename) + + return nil +} + +// SavePrivateKeyToFile 将私钥保存到文件 +func (c *CertKey) SavePrivateKeyToFile(filename string) error { + err := ioutil.WriteFile(c.SavePath+"/"+filename, c.KeyRaw, 0600) + if err != nil { + logrus.Errorf("Faile to save %s: %v", filename, err) + return err + } + + logrus.Infof("Successfully saved %s", filename) + + return nil +} diff --git a/pkg/deployer/clusterInfo.go b/pkg/deployer/clusterInfo.go new file mode 100755 index 0000000000000000000000000000000000000000..3964b25f6db4ec986664745d2b845b0504e7f8fc --- /dev/null +++ b/pkg/deployer/clusterInfo.go @@ -0,0 +1,40 @@ +package deployer + + +type ClusterInfo struct { + Name string `yaml:"name"` + ApiEndpoint string `yaml:"api_endpoint"` + Kubernetes Kubernetes `yaml:"kubernetes"` + ClusterCIDR string `yaml:"cluster_cidr"` + PodCIDR string `yaml:"pod_cidr"` + ServiceCIDR string `yaml:"service_cidr"` + InfraDriver string `yaml:"infra_driver"` + OsType string `yaml:"os_type"` + OsImage string `yaml:"os_image"` + OsConfigList []OsConfig `yaml:"os_config_list"` + EtcdType string `yaml:"etcd_type"` +} + +type Kubernetes struct { + KubernetesVersion string `yaml:"kubernetes_version"` +} + +type OsConfig struct { + Roles []string `yaml:"roles"` + DiskSize string `yaml:"disk_size"` + Image string `yaml:"image"` + MemorySize string `yaml:"memory_size"` + Cpus int `yaml:"cpus"` +} + + + + +func ParseFromFile(filename string) ClusterInfo{ + return ClusterInfo{} +} + + +func (* ClusterInfo)GenerateToFile(filename string) error{ + return nil +} \ No newline at end of file diff --git a/pkg/deployer/phase/base.go b/pkg/deployer/phase/base.go new file mode 100755 index 0000000000000000000000000000000000000000..67d3ce8598dca90077d362c02907475c2fcc0ac5 --- /dev/null +++ b/pkg/deployer/phase/base.go @@ -0,0 +1,5 @@ +package phase + +type Phase interface { + Do() +} \ No newline at end of file diff --git a/pkg/deployer/phase/generate.go b/pkg/deployer/phase/generate.go new file mode 100755 index 0000000000000000000000000000000000000000..4d7161ef887f0b075c07331ede08c646f926d718 --- /dev/null +++ b/pkg/deployer/phase/generate.go @@ -0,0 +1,21 @@ +package phase + +import "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra" + + +type GeneratePhase struct{ + Mode string // master, node +} + +func (p GeneratePhase)GenerateAssets() infra.Assets{ + //managers = [ + //"etcd", + //"manifests", + //"certs", + //] + //assets = dict() + //for i in managers: + // assets.merge(i.generateAssets()) + //return assets + return infra.Assets{} +} diff --git a/pkg/deployer/phase/init.go b/pkg/deployer/phase/init.go new file mode 100755 index 0000000000000000000000000000000000000000..b0e860c0ded43c58e764fc964e2dc9e639997128 --- /dev/null +++ b/pkg/deployer/phase/init.go @@ -0,0 +1,15 @@ +package phase + +import "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra" + +type InitPhase struct { +} + +func (p InitPhase) Do() { + generator := infra.GetBootConfigAssembler(clusterInfo.OsType) + masterInitConfig := generator.Assemble(generateInitAssets()) + masterSpec := parseSpecFromClusterInfo(clusterInfo) + _ = infraDeployer.Create(masterSpec, masterInitConfig) + apiMonitor.WaitForMastersReady(0) + +} \ No newline at end of file diff --git a/pkg/deployer/phase/join.go b/pkg/deployer/phase/join.go new file mode 100755 index 0000000000000000000000000000000000000000..367bfa5fb68b3cc8567b13a798e5d7ff93f6abe1 --- /dev/null +++ b/pkg/deployer/phase/join.go @@ -0,0 +1 @@ +package phase diff --git a/pkg/deployer/playbook/base.go b/pkg/deployer/playbook/base.go new file mode 100755 index 0000000000000000000000000000000000000000..c94508838fee2ca6d10c6df1c340a23f2927bd3b --- /dev/null +++ b/pkg/deployer/playbook/base.go @@ -0,0 +1,28 @@ +package playbook + +import ( + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/deployer" + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/deployer/phase" +) + +type Playbook struct { + clusterInfo deployer.ClusterInfo + phases []phase.Phase +} + + +func (p Playbook) Start() { + for _, i := range p.phases{ + p.Run(i) + } +} + + +func (p *Playbook) AddPhase(phase phase.Phase){ + p.phases = append(p.phases, phase) +} + + +func (p Playbook) Run(phase phase.Phase) { + +} \ No newline at end of file diff --git a/pkg/deployer/playbook/oneshot.go b/pkg/deployer/playbook/oneshot.go new file mode 100755 index 0000000000000000000000000000000000000000..d0a498ce7af40a2013e2f881099175742f3f08b6 --- /dev/null +++ b/pkg/deployer/playbook/oneshot.go @@ -0,0 +1,15 @@ +package playbook + +import "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/deployer/phase" + +func OneshotPlaybook() Playbook{ + return Playbook{ + phase.InitPhase{}, + } +} + +//managers = [ +//"etcd", +//"manifests", +//"certs", +//] \ No newline at end of file diff --git a/pkg/deployer/playbook/upgrade.go b/pkg/deployer/playbook/upgrade.go new file mode 100755 index 0000000000000000000000000000000000000000..db1daafb46896cb1337e4c77ba4fe0eed217e8f4 --- /dev/null +++ b/pkg/deployer/playbook/upgrade.go @@ -0,0 +1,6 @@ +package playbook + +type UpgradePlabook struct { + +} + diff --git a/pkg/deployer/state.go b/pkg/deployer/state.go new file mode 100755 index 0000000000000000000000000000000000000000..9b7a6f998fe54bf70059b619ff4f99d82aeba7bf --- /dev/null +++ b/pkg/deployer/state.go @@ -0,0 +1,5 @@ +package deployer + +const ( + state_ready = iota +) diff --git a/pkg/deployer/template.go b/pkg/deployer/template.go new file mode 100755 index 0000000000000000000000000000000000000000..c1d01d1e7736009bd3aea6f0388989bc5b9e5c49 --- /dev/null +++ b/pkg/deployer/template.go @@ -0,0 +1,10 @@ +package deployer + +import ( + "github.com/pkg/errors" + "strings" + "text/template" + + "github.com/lithammer/dedent" +) + diff --git a/pkg/deployer/utils.go b/pkg/deployer/utils.go new file mode 100755 index 0000000000000000000000000000000000000000..fc18c22fd8cb37040ed63424f942d88034a30c28 --- /dev/null +++ b/pkg/deployer/utils.go @@ -0,0 +1,17 @@ +package deployer + +// 通过grpc请求handler完成操作 + +func DownloadFile(content, path string){ + +} + + +func PullImage(){ + +} + + +func WriteConfig(content, path string){ + +} diff --git a/pkg/deployer/workflow.go b/pkg/deployer/workflow.go new file mode 100755 index 0000000000000000000000000000000000000000..e4e5f4bd31f3028283a848cd357060f7105b1393 --- /dev/null +++ b/pkg/deployer/workflow.go @@ -0,0 +1,74 @@ +package deployer + +import ( + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg" + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/deployer/phase" + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra" +) +// TODO 断点续传机制 + +func InitCluster(filename string) error { + // TODO cluster和vm的状态机制, init, pending, + // cluster state: pending, running, stopped + // infra state: none, pending, created, starting, running, stopping, stopped + // k8s state: TODO + + + clusterInfo := ParseFromFile(filename) + infraDeployer := infra.GetInfraDeployer(clusterInfo.InfraDriver) + bootConfigAssembler := infra.GetBootConfigAssembler(clusterInfo.InfraDriver) + + infraSpec := parseSpecFromClusterInfo(clusterInfo) + assets := generateInitAssets() + bootConfig := bootConfigAssembler.Assemble(assets) + _ = infraDeployer.Create(infraSpec, bootConfig) + apiMonitor := pkg.ApiMonitor{clusterInfo.ApiEndpoint} + apiMonitor.WaitForMastersReady(0) + return nil +} + + + +func JoinToCluster(filename string) { + clusterInfo := ParseFromFile(filename) + infraDeployer := infra.GetInfraDeployer(clusterInfo.InfraDriver) + apiMonitor := pkg.ApiMonitor{clusterInfo.ApiEndpoint} + + generator := infra.GetBootConfigAssembler(clusterInfo.InfraDriver) + initConfig := generator.Assemble(generateJoinAssets()) + spec := parseSpecFromClusterInfo(clusterInfo) + _ = infraDeployer.Create(spec, initConfig) + apiMonitor.WaitForWorkersReady(0) +} + +func OneShot(filename string){ + clusterInfo := ParseFromFile(filename) + infraDeployer := infra.GetInfraDeployer(clusterInfo.InfraDriver) + apiMonitor := pkg.ApiMonitor{clusterInfo.ApiEndpoint} + + generatePhase := phase.GeneratePhase{"master"} + assets := generatePhase.GenerateAssets() + + generator := infra.GetBootConfigAssembler(clusterInfo.OsType) + masterInitConfig := generator.Assemble(assets) + masterSpec := parseSpecFromClusterInfo(clusterInfo) + _ = infraDeployer.Create(masterSpec, masterInitConfig) + apiMonitor.WaitForMastersReady(0) + + + initConfig := generator.Assemble(generateJoinAssets()) + spec := parseSpecFromClusterInfo(clusterInfo) + _ = infraDeployer.Create(spec, initConfig) + apiMonitor.WaitForWorkersReady(0) + +} + +func parseSpecFromClusterInfo(info ClusterInfo) infra.InfraSpec { + return infra.InfraSpec{} +} + + + +func generateJoinAssets() infra.Assets{ + return infra.Assets{} +} diff --git a/pkg/infra/assets/assets.go b/pkg/infra/assets/assets.go new file mode 100755 index 0000000000000000000000000000000000000000..81fa8cd1d49efc24f6a434fcc91820b381a3265f --- /dev/null +++ b/pkg/infra/assets/assets.go @@ -0,0 +1,22 @@ +/* +Copyright 2023 KylinSoft Co., 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 assets + +type File struct { + Filename string + Data []byte +} diff --git a/pkg/infra/assets/cluster/cluster.go b/pkg/infra/assets/cluster/cluster.go new file mode 100644 index 0000000000000000000000000000000000000000..5b38e6ae003e70cf9a3eb365922f739c7e84155b --- /dev/null +++ b/pkg/infra/assets/cluster/cluster.go @@ -0,0 +1,150 @@ +/* +Copyright 2023 KylinSoft Co., 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 cluster + +import ( + "os" + "path/filepath" + + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra/assets" + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra/terraform" + "github.com/hashicorp/terraform-exec/tfexec" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +/* + installDir:自定义工作目录,在该路径下自动创建terraform文件夹 + dir:该路径存放tf配置文件 + terraformDir:该路径包含bin、plugins目录,存放terraform执行文件以及所需plugins +*/ + +type InfraProvider interface { + Create() + Destroy() +} + +type Cluster struct { + InstallDir string + Platform string + Name string +} + +// TODO: 配置文件准备阶段 + +func (c *Cluster) Create() error { + terraformDir := filepath.Join(c.InstallDir, "terraform") + dir := filepath.Join(terraformDir, c.Platform, c.Name) + + terraformVariables := &TerraformVariables{} + tfvarsFiles := make([]*assets.File, 0, len(terraformVariables.Files())+len(c.Platform)+len(c.Name)) + tfvarsFiles = append(tfvarsFiles, terraformVariables.Files()...) + + logrus.Infof("start to create %s in %s", c.Name, c.Platform) + + outputs, err := c.applyStage(dir, terraformDir, tfvarsFiles) + if err != nil { + return errors.Wrapf(err, "failed to create %s in %s", c.Name, c.Platform) + } + + logrus.Info(string(outputs.Data)) + logrus.Infof("succeed in creating %s in %s", c.Name, c.Platform) + + return nil +} + +func (c *Cluster) applyStage(dir string, terraformDir string, tfvarsFiles []*assets.File) (*assets.File, error) { + var applyOpts []tfexec.ApplyOption + for _, file := range tfvarsFiles { + if err := os.WriteFile(filepath.Join(dir, file.Filename), file.Data, 0o600); err != nil { + return nil, err + } + applyOpts = append(applyOpts, tfexec.VarFile(filepath.Join(dir, file.Filename))) + } + + return c.applyTerraform(dir, terraformDir, applyOpts...) +} + +func (c *Cluster) applyTerraform(dir string, terraformDir string, applyOpts ...tfexec.ApplyOption) (*assets.File, error) { + applyErr := terraform.TFApply(dir, terraformDir, applyOpts...) + + _, err := os.Stat(filepath.Join(dir, "terraform.tfstate")) + if os.IsNotExist(err) { + logrus.Errorf("Failed to read tfstate: %v", err) + return nil, errors.Wrap(err, "failed to read tfstate") + } + + if applyErr != nil { + return nil, errors.WithMessage(applyErr, "failed to apply Terraform") + } + + outputs, err := terraform.Outputs(dir, terraformDir) + if err != nil { + return nil, errors.Wrap(err, "could not get outputs file") + } + + outputsFile := &assets.File{ + Filename: "outputs", + Data: outputs, + } + + return outputsFile, nil +} + +func (c *Cluster) Destroy() error { + terraformDir := filepath.Join(c.InstallDir, "terraform") + dir := filepath.Join(terraformDir, c.Platform, c.Name) + + logrus.Infof("start to destroy %s in %s", c.Name, c.Platform) + + // Question: Destroy的tfvarsFiles的获取 + + terraformVariables := &TerraformVariables{} + tfvarsFiles := make([]*assets.File, 0, len(terraformVariables.Files())+len(c.Platform)+len(c.Name)) + tfvarsFiles = append(tfvarsFiles, terraformVariables.Files()...) + + err := c.destroyStage(dir, terraformDir, tfvarsFiles) + if err != nil { + return errors.Wrapf(err, "failed to destroy %s in %s", c.Name, c.Platform) + } + os.Remove(dir) + + logrus.Infof("succeed in destroying %s in %s", c.Name, c.Platform) + + return nil +} + +func (c *Cluster) destroyStage(dir string, terraformDir string, tfvarsFiles []*assets.File) error { + var destroyOpts []tfexec.DestroyOption + for _, file := range tfvarsFiles { + if err := os.WriteFile(filepath.Join(dir, file.Filename), file.Data, 0o600); err != nil { + return err + } + destroyOpts = append(destroyOpts, tfexec.VarFile(filepath.Join(dir, file.Filename))) + } + + return destroyTerraform(dir, terraformDir, destroyOpts...) +} + +func destroyTerraform(dir string, terraformDir string, destroyOpts ...tfexec.DestroyOption) error { + destroyErr := terraform.TFDestroy(dir, terraformDir, destroyOpts...) + if destroyErr != nil { + return errors.WithMessage(destroyErr, "failed to destroy Terraform") + } + + return nil +} diff --git a/pkg/infra/assets/cluster/tfvars.go b/pkg/infra/assets/cluster/tfvars.go new file mode 100644 index 0000000000000000000000000000000000000000..a2f1b17960044c8c61988319f7f092883fdaa740 --- /dev/null +++ b/pkg/infra/assets/cluster/tfvars.go @@ -0,0 +1,83 @@ +/* +Copyright 2023 KylinSoft Co., 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 cluster + +import ( + "encoding/json" + "html/template" + "os" + "path/filepath" + + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra/assets" + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra/tfvars/openstack" + "github.com/pkg/errors" +) + +type TerraformVariables struct { + FileList []*assets.File +} + +func (t *TerraformVariables) Files() []*assets.File { + return t.FileList +} + +func Generate() error { + // 从文件中读取 jsonData + jsonData, err := os.ReadFile("data/json/openstack/main.tf.json") + if err != nil { + return errors.Wrap(err, "error reading json data") + } + + // 解析 JSON 数据 + var terraformData openstack.TerraformData + err = json.Unmarshal(jsonData, &terraformData) + if err != nil { + return errors.Wrap(err, "error parsing json data") + } + + // 从文件中读取 terraformConfig + terraformConfig, err := os.ReadFile("data/templates/openstack/main.tf.template") + if err != nil { + return errors.Wrap(err, "error reading terraform config template") + } + + // 使用模板填充数据 + tmpl, err := template.New("terraform").Parse(string(terraformConfig)) + if err != nil { + return errors.Wrap(err, "error creating terraform config template") + } + + // 创建一个新的文件用于写入填充后的数据 + tfDir := filepath.Join("/root", "terraform") + if err := os.MkdirAll(tfDir, os.ModePerm); err != nil { + return errors.Wrap(err, "could not create the terraform directory") + } + + outputFile, err := os.Create(filepath.Join(tfDir, "main.tf")) + if err != nil { + return errors.Wrap(err, "error creating terraform config") + } + defer outputFile.Close() + + // 将填充后的数据写入文件 + err = tmpl.Execute(outputFile, terraformData) + if err != nil { + return errors.Wrap(err, "error executing terraform config") + } + + return nil +} diff --git a/pkg/infra/terraform/init.go b/pkg/infra/terraform/init.go new file mode 100644 index 0000000000000000000000000000000000000000..cbd3ad30e41c28e49cf4637dcd737a35579ed3e8 --- /dev/null +++ b/pkg/infra/terraform/init.go @@ -0,0 +1,41 @@ +/* +Copyright 2023 KylinSoft Co., 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 terraform + +import ( + "context" + "path/filepath" + + "github.com/hashicorp/terraform-exec/tfexec" + "github.com/pkg/errors" +) + +// terraform init +func TFInit(dir string, terraformDir string) error { + tf, err := newTFExec(dir, terraformDir) + if err != nil { + return errors.Wrap(err, "failed to create a new tfexec") + } + + // 使用本地terraform插件 + err = tf.Init(context.Background(), tfexec.PluginDir(filepath.Join(terraformDir, "plugins"))) + if err != nil { + return errors.Wrap(err, "failed to init terraform") + } + + return nil +} diff --git a/pkg/infra/terraform/logger.go b/pkg/infra/terraform/logger.go new file mode 100644 index 0000000000000000000000000000000000000000..54add90bd6e1daa6d645206dae61d1cf1f201a2c --- /dev/null +++ b/pkg/infra/terraform/logger.go @@ -0,0 +1,37 @@ +/* +Copyright 2023 KylinSoft Co., 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 terraform + +import ( + "github.com/sirupsen/logrus" +) + +type printfer struct { + logger *logrus.Logger + level logrus.Level +} + +func newPrintfer() *printfer { + return &printfer{ + logger: logrus.StandardLogger(), + level: logrus.DebugLevel, + } +} + +func (p *printfer) Printf(format string, ifs ...interface{}) { + p.logger.Logf(p.level, format, ifs...) +} diff --git a/pkg/infra/terraform/providers/providers.go b/pkg/infra/terraform/providers/providers.go new file mode 100644 index 0000000000000000000000000000000000000000..986ee8aa947bbe9f3e5489758a4358ae1c29e847 --- /dev/null +++ b/pkg/infra/terraform/providers/providers.go @@ -0,0 +1,35 @@ +/* +Copyright 2023 KylinSoft Co., 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 providers + +import "fmt" + +var ( + OpenStack = provider("openstack") +) + +type Provider struct { + Name string + Source string +} + +func provider(name string) Provider { + return Provider{ + Name: name, + Source: fmt.Sprintf("nkd/local/%s", name), + } +} diff --git a/pkg/infra/terraform/stage.go b/pkg/infra/terraform/stage.go new file mode 100644 index 0000000000000000000000000000000000000000..dabe48f264dc8c373c2f1b1ba4ceb43a7a1ba9ec --- /dev/null +++ b/pkg/infra/terraform/stage.go @@ -0,0 +1,26 @@ +/* +Copyright 2023 KylinSoft Co., 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 terraform + +import "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra/terraform/providers" + +type Stage interface { + Name() string + + // the list of providers that are used for the stage + Providers() []providers.Provider +} diff --git a/pkg/infra/terraform/stages/openstack/stages.go b/pkg/infra/terraform/stages/openstack/stages.go new file mode 100644 index 0000000000000000000000000000000000000000..1b1f18a23f8bbb8c27f2f1ef55b726ba8c790bd6 --- /dev/null +++ b/pkg/infra/terraform/stages/openstack/stages.go @@ -0,0 +1,19 @@ +package openstack + +import ( + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra/terraform" + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra/terraform/providers" + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra/terraform/stages" +) + +var PlatformStages = []terraform.Stage{} + +func AddPlatformStage(name string) { + newStage := stages.NewStage( + "openstack", + name, + []providers.Provider{providers.OpenStack}, + ) + + PlatformStages = append(PlatformStages, newStage) +} diff --git a/pkg/infra/terraform/stages/platform/stages.go b/pkg/infra/terraform/stages/platform/stages.go new file mode 100644 index 0000000000000000000000000000000000000000..7a99cad051b99d1645a50c8fd0a89232b5fe5421 --- /dev/null +++ b/pkg/infra/terraform/stages/platform/stages.go @@ -0,0 +1,19 @@ +package platform + +import ( + "errors" + "fmt" + + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra/terraform" + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra/terraform/stages/openstack" +) + +func StagesForPlatform(platform string, stage string) ([]terraform.Stage, error) { + switch platform { + case "openstack": + openstack.AddPlatformStage(stage) + return openstack.PlatformStages, nil + default: + return nil, errors.New(fmt.Sprintf("unsupported platform %q", platform)) + } +} diff --git a/pkg/infra/terraform/stages/split.go b/pkg/infra/terraform/stages/split.go new file mode 100644 index 0000000000000000000000000000000000000000..caf0de7e1889f51cdc6f176149899e5f6e6e47f1 --- /dev/null +++ b/pkg/infra/terraform/stages/split.go @@ -0,0 +1,43 @@ +package stages + +import ( + "fmt" + + "gitee.com/openeuler/nestos-kubernetes-deployer/pkg/infra/terraform/providers" +) + +type StageOption func(*SplitStage) + +func NewStage(platform, name string, providers []providers.Provider, opts ...StageOption) SplitStage { + s := SplitStage{ + platform: platform, + name: name, + providers: providers, + } + for _, opt := range opts { + opt(&s) + } + return s +} + +type SplitStage struct { + platform string + name string + providers []providers.Provider +} + +func (s SplitStage) Name() string { + return s.name +} + +func (s SplitStage) Providers() []providers.Provider { + return s.providers +} + +func (s SplitStage) StateFilename() string { + return fmt.Sprintf("terraform.%s.tfstate", s.name) +} + +func (s SplitStage) OutputsFilename() string { + return fmt.Sprintf("%s.tfvars.json", s.name) +} diff --git a/pkg/infra/terraform/state.go b/pkg/infra/terraform/state.go new file mode 100644 index 0000000000000000000000000000000000000000..9509abc2d818ca9cf9c59d5959e96c7cc19cd009 --- /dev/null +++ b/pkg/infra/terraform/state.go @@ -0,0 +1,51 @@ +/* +Copyright 2023 KylinSoft Co., 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 terraform + +import ( + "context" + "encoding/json" + + "github.com/pkg/errors" +) + +const StateFilename = "terraform.tfstate" + +// Reads the terraform state file. +func Outputs(workingDir string, terraformBinary string) ([]byte, error) { + tf, err := newTFExec(workingDir, terraformBinary) + if err != nil { + return nil, err + } + + tfoutput, err := tf.Output(context.Background()) + if err != nil { + return nil, errors.Wrap(err, "failed to read terraform state file") + } + + outputs := make(map[string]interface{}, len(tfoutput)) + for key, value := range tfoutput { + outputs[key] = value.Value + } + + data, err := json.Marshal(outputs) + if err != nil { + return nil, errors.Wrap(err, "could not marshal outputs") + } + + return data, nil +} diff --git a/pkg/infra/terraform/terraform.go b/pkg/infra/terraform/terraform.go new file mode 100644 index 0000000000000000000000000000000000000000..6bb6a9cf819112a67d98f18d87a498162fba7a25 --- /dev/null +++ b/pkg/infra/terraform/terraform.go @@ -0,0 +1,98 @@ +/* +Copyright 2023 KylinSoft Co., 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 terraform + +import ( + "context" + "os" + "path/filepath" + "runtime" + + "github.com/hashicorp/terraform-exec/tfexec" + "github.com/openshift/installer/pkg/lineprinter" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func newTFExec(dir string, terraformDir string) (*tfexec.Terraform, error) { + tfPath := filepath.Join(terraformDir, "bin", runtime.GOOS+"_"+runtime.GOARCH, "terraform") + tf, err := tfexec.NewTerraform(dir, tfPath) + if err != nil { + return nil, err + } + + // If the log path is not set, terraform will not receive debug logs. + if logPath, ok := os.LookupEnv("TERRAFORM_LOG_PATH"); ok { + if err := tf.SetLog(os.Getenv("TERRAFORM_LOG")); err != nil { + logrus.Infof("Skipping setting terraform log levels: %v", err) + } else { + tf.SetLogCore(os.Getenv("TERRAFORM_LOG_CORE")) //nolint:errcheck + tf.SetLogProvider(os.Getenv("TERRAFORM_LOG_PROVIDER")) //nolint:errcheck + tf.SetLogPath(logPath) //nolint:errcheck + } + } + + // Add terraform info logs to the installer log + lpDebug := &lineprinter.LinePrinter{Print: (&lineprinter.Trimmer{WrappedPrint: logrus.Debug}).Print} + lpError := &lineprinter.LinePrinter{Print: (&lineprinter.Trimmer{WrappedPrint: logrus.Error}).Print} + defer lpDebug.Close() + defer lpError.Close() + + tf.SetStdout(lpDebug) + tf.SetStderr(lpError) + tf.SetLogger(newPrintfer()) + + return tf, nil +} + +// terraform apply +func TFApply(dir string, terraformDir string, applyOpts ...tfexec.ApplyOption) error { + if err := TFInit(dir, terraformDir); err != nil { + return err + } + + tf, err := newTFExec(dir, terraformDir) + if err != nil { + return errors.Wrap(err, "failed to create a new tfexec") + } + + err = tf.Apply(context.Background(), applyOpts...) + if err != nil { + return errors.Wrap(err, "failed to apply Terraform") + } + + return nil +} + +// terraform destroy +func TFDestroy(dir string, terraformDir string, destroyOpts ...tfexec.DestroyOption) error { + if err := TFInit(dir, terraformDir); err != nil { + return err + } + + tf, err := newTFExec(dir, terraformDir) + if err != nil { + return errors.Wrap(err, "failed to destroy a new tfexec") + } + + err = tf.Destroy(context.Background(), destroyOpts...) + if err != nil { + return errors.Wrap(err, "failed to destroy terraform") + } + + return nil +} diff --git a/pkg/infra/tfvars/openstack/tfvars.go b/pkg/infra/tfvars/openstack/tfvars.go new file mode 100644 index 0000000000000000000000000000000000000000..693bee2e0d0779a7f6747b1617a8103ef06b4cee --- /dev/null +++ b/pkg/infra/tfvars/openstack/tfvars.go @@ -0,0 +1,44 @@ +/* +Copyright 2023 KylinSoft Co., 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 openstack + +// 定义 JSON 数据的结构 +type TerraformData struct { + Openstack struct { + User_name string `json:"user_name"` + Password string `json:"password"` + Tenant_name string `json:"tenant_name"` + Auth_url string `json:"auth_url"` + Region string `json:"region"` + } `json:"openstack"` + Flavor struct { + Name string `json:"name"` + Ram string `json:"ram"` + Vcpus string `json:"vcpus"` + Disk string `json:"disk"` + Is_public string `json:"is_public"` + } `json:"flavor"` + Instance struct { + Count string `json:"count"` + Name string `json:"name"` + Image_name string `json:"image_name"` + Key_pair string `json:"key_pair"` + } `json:"instance"` + Floatip struct { + Pool string `json:"pool"` + } `json:"floatip"` +} diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/pkg/osimage/osimage.go b/pkg/osimage/osimage.go new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/pkg/registry/registry.go b/pkg/registry/registry.go new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391