├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── app ├── cluster │ └── cluster.go ├── resource │ └── resource.go ├── source │ ├── aks │ │ └── aks.go │ ├── gke │ │ └── gke.go │ ├── kops │ │ └── kops.go │ ├── source.go │ └── source_impl │ │ └── source_impl.go └── target │ ├── eks │ └── eks.go │ ├── target.go │ └── target_impl │ └── target_impl.go ├── config.ini ├── controllers └── MIGRATE_IMAGES │ └── MIGRATE_IMAGES_controller.go ├── docs ├── kmf-architecture.jpg └── kubernetes-migration-CLI-Logic.jpg ├── go.mod ├── go.sum └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | # Binaries for programs and plugins 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | 9 | # Test binary, built with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # Dependency directories (remove the comment below to include it) 16 | # vendor/ 17 | 18 | containers-migration-factory -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes Migration Factory User Guide 2 | 3 | Kubernetes Migrations Factory (KMF) is a tool developed for migrating Kubernetes workloads to Amazon EKS. KMF is written in [Golang](https://golang.org/) and offers command line interface. The Kubernetes Migration Factory solution is an orchestration platform for migrating containers to Amazon EKS at 4 | scale. It is designed to coordinate and automate many of the manual processes, eliminating human error and speeding migration phases down to minutes from weeks of planning and data collection. With just a few inputs provided to the KMF command line interface(CLI), customers will be able to set, launch, and track the migration plan for their current container platform configuration into Amazon EKS. This solution is able to scan the source platform and generate a blueprint of the running workload. After the scan is done, you can also see list of all the Kubernetes resources that will be created onthe destination. This solution is built to understand the source platform implementation details and then manipulate the obtained manifest files such that it is valid and accepted by the destination Kubernetes implementation, Amazon EKS. It is able to provide recommendations to add relevant AWS native services by determining the type of services used in the source and finding a similar type of AWS resources. The solution will enable the customer with autonomous recommendation for their data plane setup based on their current cluster meta data. 5 | 6 | ### **Architecture** 7 | 8 | ![Architecture](docs/kmf-architecture.jpg) 9 | 10 | ![KMF-CLI Logic](docs/kubernetes-migration-CLI-Logic.jpg) 11 | Kubernetes Migration Factory (KMF) tool can be used to migrate Kubernetes resources from a Kubernetes cluster running anywhere(1) to Amazon EKS via a Command Line Interface(CLI). It can run on a server that can authenticate to the source Kubernetes cluster and target Kubernetes cluster, which is Amazon EKS in this case. 12 | 13 | ### **Prerequisites** 14 | 15 | * This tool migrates from any Kubernetes cluster to Amazon EKS, so it needs a destination EKS Cluster. For more information, please refer [Creating an Amazon EKS Cluster](https://docs.aws.amazon.com/eks/latest/userguide/create-cluster.html) documentation 16 | * On the workstation where the KMF CLI will be used, setup the kubeconfig for the destination cluster. For more information on authenticating and accessing an Amazon EKS cluster, please refer to [Cluster Authentication](https://docs.aws.amazon.com/eks/latest/userguide/managing-auth.html) documentation 17 | * Setup kubeconfig file to access the EKS Cluster using the kubectl tool and the same setup is sufficient for KMF to get access. Please refer [setting kubeconfig file for EKS cluster access](https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html) documentation 18 | * Similarly setup the kubeconfig file for the source Kubernetes cluster. For Google Kubernetes Engine(GKE), you may refer [cluster access to GKE](https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl) just like how you setup access for kubectl tool 19 | * Download `KMF` for your platform from here [TO COME]. If your platform is not among the available releases, you can install GoLang and setup the workspace. Please refer [Download and Install](https://golang.org/doc/install) for more information about installing golang to your workstation 20 | * Amazon EKS Cluster used as destination for migrating the Kubernetes workload should have access to the docker registry used in the source. You may follow the documentation [Pull an Image from a Private Registry 21 | ](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). If you create the secret for private registry access in the source kubernetes cluster and also attach to all the container specifications in all resources, kubernetes migration factory will migrate that as well 22 | * KMF tool also helps to migrate images from 3rd party repositories such as GCR, Dockerhub, Gitlab private registry to Amazon Elastic Container Registry. On the workstation where the KMF CLI will be used, ensure to setup docker login for Amazon ECR and also add docker login to list of supported repositories (GCR, Gitlab, MCR, Dockerhub) that are intended for migration prior to executing KMF 23 | * KMF tool uses aws privileges assigned to the execution id in order to create Amazon elastic container registry and push images to it. The following IAM policy statement is the minimum required permission 24 | 25 | { 26 | "Version":"2012-10-17", 27 | "Statement":[ 28 | { 29 | "Sid":"ListImagesInRepository", 30 | "Effect":"Allow", 31 | "Action":[ 32 | "ecr:ListImages" 33 | ], 34 | "Resource":"arn:aws:ecr:::repository/*" 35 | }, 36 | { 37 | "Sid":"GetAuthorizationToken", 38 | "Effect":"Allow", 39 | "Action":[ 40 | "ecr:GetAuthorizationToken" 41 | ], 42 | "Resource":"*" 43 | }, 44 | { 45 | "Sid":"ManageRepositoryContents", 46 | "Effect":"Allow", 47 | "Action":[ 48 | "ecr:BatchCheckLayerAvailability", 49 | "ecr:GetDownloadUrlForLayer", 50 | "ecr:GetRepositoryPolicy", 51 | "ecr:DescribeRepositories", 52 | "ecr:ListImages", 53 | "ecr:DescribeImages", 54 | "ecr:BatchGetImage", 55 | "ecr:InitiateLayerUpload", 56 | "ecr:UploadLayerPart", 57 | "ecr:CompleteLayerUpload", 58 | "ecr:PutImage" 59 | ], 60 | "Resource":"arn:aws:ecr:::repository/*" 61 | } 62 | ] 63 | } 64 | 65 | ### **KMF CLI Setup** 66 | 67 | **Use an already build binary:** 68 | **Steps:** 69 | 70 | 1. Download KMF CLI for your os from the release section to your workstation where you have authenticated to both source and destination clusters. There are compiled binaries for Mac OS, Linux and Windows operating system. 71 | * For Mac OS, the binary name is kmf_mac. This document is narrated to reference binary by the name kmf, hence edit the commands accrodingly. 72 | * For Linux OS, the binary name is kmf. 73 | * For Windows OS, the binary name kmf.exe. This document is narrated to reference binary by the name kmf, hence edit the commands accrodingly. 74 | 75 | **Note**: The compiled binaries for 64 bit executables with GOARCH=amd64 76 | go build commands used are as below for compilation: 77 | 78 | For linux: ```CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ./bin/linux/kmf``` 79 | 80 | For windows ```CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o ./bin/windows/kmf.exe``` 81 | 82 | For mac ```go build -o ./bin/mac/kmf``` since it was compiled on a mac machine. 83 | 84 | For more information you may refer [cross-compiling made easy with Golang](https://opensource.com/article/21/1/go-cross-compiling) documentation 85 | 86 | 2. Optional: Add the tool to your OS PATH 87 | 88 | 89 | **Building the binary from source: 90 | Steps:** 91 | 92 | 1. Clone the kubernetes-migration-factory repository 93 | 2. Change directory to containers-migration-factory 94 | 95 | ``` 96 | $cd kubernetes-migration-factory/ 97 | ``` 98 | 99 | 1. Run the below command to build the binary for the KMF 100 | 101 | ``` 102 | $go build 103 | ``` 104 | 105 | The above command will build the binary for KMF CLI within current directory to a file named `containers-migration-factory` 106 | If you want this to be a shorter name say `kmf`, then run the command as below: 107 | 108 | ``` 109 | $go build -o ./bin/kmf 110 | ``` 111 | 112 | ### **Running KMF CLI:** 113 | ### **Running with a Config config.ini** 114 | From the operating system path from where you are making calls to ./bin/kmf, make sure that you have a file with the name exactly called [config.ini](/config.ini) and provide all the required information. A sample config.ini file looks like below: 115 | 116 | `Explanation for each parameter is provided in the config.ini file as comments.` 117 | 118 | ``` 119 | [COMMON] 120 | # common configuration params required for migration. 121 | # Local path where generated helm charts to be saved 122 | HELM_CHARTS_PATH=/Users/username/kuberenetes-pocs/helm 123 | RESOURCES=all 124 | # Valid Value for ACTION Deploy/Delete 125 | ACTION=Delete 126 | # Namespaces from which the resources need to migrated 127 | # comma seperated list of namespace or "all" 128 | NAMESPACES=all 129 | 130 | [SOURCE] 131 | # Source Cloud Provider valid values are GKE,AKE,KOPS 132 | CLOUD=GKE 133 | # Source kube config file 134 | # Refer the documentation for the respective source cluster provider to create the kubeconfig file. For GKE, you may refer https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl 135 | KUBE_CONFIG=/Users/username/.kube/gcp.config 136 | CONTEXT= 137 | 138 | [TARGET] 139 | # Target Cloud Provider valid value is AWS only 140 | CLOUD=AWS 141 | # Target kube config file 142 | # Refer the AWS documentation for creating the kubeconfig file https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html 143 | KUBE_CONFIG=/Users/username/.kube/config 144 | CONTEXT= 145 | 146 | [MIGRATE_IMAGES] 147 | # This section is used for passing values to the KMF CLI to perform container registry images migration to Amazon ECR and this is optional 148 | # Do you wish to migrate images from 3rd party repositories to Amazon Elastic Container Registry? Supply either "Yes" or "No" 149 | USERCONSENT=Yes 150 | # Comma separated list of 3rd party registries. Tool supports migration from gcr, gitlab, mcr, dockerhub registries. 151 | REGISTRY=GCR 152 | ``` 153 | ### **Explanation of each supported parameter for the KMF CLI tool** 154 | 155 | ### **COMMON Section** 156 | #### common configuration params required for migration 157 | 158 | **HELM_CHARTS_PATH** (Required): Local path to store the Helm charts obtained from the source cluster 159 | example value /Users/username/kuberenetes-pocs/helm 160 | 161 | 162 | **RESOURCES** (Required): Kubernetes resources to migrate from source to destination cluster 163 | valid values are: 164 | * "all" for all resources supported by KMF CLI 165 | * Comma separated resources, for example "services, deployments, cronjobs" 166 | 167 | **Action** (Required) : Action to perform on the destination cluster 168 | valid values are: 169 | ***ACTION=Delete:*** To delete the kubernetes resource matching the source cluster 170 | Note: Use this only if it is necessary as this is a destruction feature 171 | ***Action=Deploy:*** To deploy the kubernetes resource matching the source cluster 172 | 173 | **Namespaces** (Required): Kubernetes Namespaces from which the KMF tool should migrate resources 174 | valid values are: "all" for migrating Kubernetes resources from all namespaces 175 | you can also provide comma separated values of namespaces, for example if the namespaced from which your want to migrate are dev, test, stage, then this will be "dev,test,stage" 176 | 177 | ### **SOURCE Section** 178 | ***CLOUD*** (Required): Cloud provider for the source Kubernetes cluster 179 | Valid values: any one of GKE, AKE, KOPS 180 | 181 | ***KUBE_CONFIG*** (Required): Kubeconfig file path on the local machine for the destination cluster 182 | 183 | ***CONTEXT*** (Required): Kubeconfig context. This helps to choose the Kubernetes cluster if there is a combined kubeconfig file with multiple clusters 184 | 185 | ### **TARGET Section** 186 | 187 | ***CLOUD*** (Required): Cloud provider for the source Kubernetes cluster 188 | Valid values: EKS 189 | 190 | ***KUBE_CONFIG*** (Required): Kubeconfig file path on the local machine for the target cluster 191 | 192 | ***CONTEXT*** (Required): Kubeconfig context. This helps to choose the Kubernetes cluster if there is a combined kubeconfig file with multiple clusters 193 | 194 | ### **MIGRATE_IMAGES Section** 195 | 196 | ***USERCONSENT*** (Required): User consent to migrate container images to Amazon ECR 197 | 198 | Valid values: Yes, No 199 | 200 | ***REGISTRY*** (Required): Source container registry name. This requires the user to perform docker login to those registry on the machine where KMF-CLI is getting executed 201 | 202 | Valid Values: gcr, gitlab, dockerhub 203 | 204 | If any argument is missing in the config.ini file or if not using a config.ini file, follow the prompt and give all the information asked. 205 | 206 | *NOTE: This tool supports a merged kubeconfig file with both the source and destination configurations. Use the same kubeconfig file location for source and destination when answering the prompts from the tool* 207 | 208 | ### **Some examples while interacting with KMF CLI** 209 | 210 | ``` 211 | $ ./bin/kmf 212 | Please pass the location of source kubernetes cluster kubeconfig file: 213 | Please pass the source context: (default: ): 214 | Please pass comma separated list of resources to migrate from source cluster to destination cluster. For all resources enter 'all': 215 | Please pass comma separated list of namespaces for source cluster. For all namespaces enter 'all': 216 | Please pass path to save Helm charts from source cluster: 217 | Do you want to migrate images from 3rd party registries to ECR? Supply either Yes or No: 218 | Tool supports migration from gcr, gitlab, mcr, dockerhub registries. Please pass comma separated list of 3rd party registries: 219 | Please pass the location of destination EKS cluster kubeconfig file: 220 | Please pass the destination context (default: ): 221 | Please pass what action the tool needs to perform. Accepted values are Deploy or Delete : 222 | ``` 223 | 224 | Based on the information provided to the CLI, it will poll the source cluster and get the details about the Kubernetes resources. KMF evaluates the source Kubernetes objects and manipulates them to be compatible with Amazon EKS, and deploys the modified manifests to its destination. 225 | 226 | ### **Running as one line:** 227 | 228 | #### **Migrating all resources from all namespaces** 229 | ``` 230 | $ ./bin/kmf --source_kubeconfig /Users//.kube/gcp.config \ 231 | --destination_kubeconfig /Users//.kube/aws.config \ 232 | --namespaces "all" \ 233 | --resources "all" \ 234 | --migrate_images "yes" \ 235 | --reg_names "gcr, dockerhub, mcr, gitlab" \ 236 | --source_context \ 237 | --destination_context \ 238 | --helm_path \ 239 | --action Deploy 240 | ``` 241 | 242 | #### **Migrating all resources from one namespace** 243 | ``` 244 | $ ./bin/kmf --source_kubeconfig /Users//.kube/gcp.config \ 245 | --destination_kubeconfig /Users//.kube/aws.config 246 | --namespaces "default" \ 247 | --resources "all" \ 248 | --migrate_images "yes" \ 249 | --reg_names "gcr, dockerhub, gitlab, mcr" \ 250 | --source_context \ 251 | --destination_context \ 252 | --helm_path \ 253 | --action Deploy 254 | ``` 255 | 256 | #### **Migrating deployments and services from multiple namepaces** 257 | ``` 258 | $ ./bin/kmf --source_kubeconfig \ 259 | /Users//.kube/gcp.config --destination_kubeconfig \ 260 | /Users//.kube/aws.config \ 261 | --namespaces "dev,default" \ 262 | --resources "deployment,service" \ 263 | --migrate_images "yes" \ 264 | --reg_names "gcr, dockerhub, gitlab, mcr" \ 265 | --source_context \ 266 | --destination_context \ 267 | --helm_path \ 268 | --action Deploy 269 | ``` 270 | 271 | #### **Migrating deployment and service resources from all namespaces** 272 | ``` 273 | $ ./bin/kmf --source_kubeconfig /Users//.kube/gcp.config \ 274 | --destination_kubeconfig /Users//.kube/aws.config \ 275 | --namespaces "all" \ 276 | --resources "deployment,service" \ 277 | --migrate_images "yes" \ 278 | --reg_names "gcr, dockerhub, gitlab, mcr" \ 279 | --source_context \ 280 | --destination_context \ 281 | --helm_path \ 282 | --action Deploy 283 | ``` 284 | 285 | ### **As of this release, the following Kubernetes Resource Types are supported by KMF:** 286 | 287 | 1. Deployments 288 | 2. Services 289 | 3. ServiceAccounts 290 | 4. Roles 291 | 5. ClusterRoles 292 | 6. RoleBindings 293 | 7. ClusterRoleBindings 294 | 8. Namespaces 295 | 9. DaemonSets 296 | 10. Secrets 297 | 11. StorageClasses 298 | 12. HorizontalPodAutoScalers 299 | 13. BatchJobs 300 | 14. PodSecurityPolicies 301 | 15. MutatingWebhookConfigurations 302 | 16. ValidatingWebhookConfigurations 303 | 17. ConfigMaps 304 | 18. PersistentVolumeClaims 305 | 19. Ingresses 306 | 20. CronJobs 307 | 21. Jobs 308 | 309 | ## **KMF integration with kubernetes cluster** 310 | 311 | KMF CLI interacts with kubernetes cluster both at source and destination by using its respective kubeconfig file. KMF loads the kubeconfig files and connected to clusters using kubernetes clientset api call. So, when it comes to integration, it is quite straight forward for customers to supply source kubeconfig and destination kubeconfig either in config.ini file or via command line arguments. 312 | 313 | ## **Limitations:** 314 | (1) Currently KMF supports migrating Kubernetes resources running on Google Kubernetes Engine (GKE) to Amazon EKS. However, slight changes to the code should allow you to use it for any other Kubernetes source. 315 | 316 | (2) KMF supports only Kubernetes resources migration, but not the data. 317 | 318 | ## **Patching & Upgrading 3rd party tools:** 319 | The list of 3rd party packages used in this tool has been listed under go.mod file. We would recommend end-users to keep track of version/feature upgrades that gets published for individual packages and decide on adopting it as they find it fit based on requirements as well as security best practice followed in their organization. Following are links to version list for each packages used, 320 | 321 | https://pkg.go.dev/cmd/go#hdr-Download_and_install_packages_and_dependencies - Golang package management 322 | https://aws.github.io/aws-sdk-go-v2/docs/ - aws sdk for go 323 | https://github.com/kubernetes/client-go - K8S Client go versioning 324 | https://pkg.go.dev/github.com/bigkevmcd/go-configparser@v0.0.0-20210106142102-909504547ead?tab=versions - Go configparse 325 | https://pkg.go.dev/github.com/ghodss/yaml@v1.0.0?tab=versions - Go yaml 326 | https://pkg.go.dev/github.com/gofrs/flock@v0.8.0?tab=versions - Go flock 327 | https://pkg.go.dev/github.com/pkg/errors@v0.9.1?tab=versions - Go errors 328 | https://pkg.go.dev/gopkg.in/yaml.v2@v2.3.0?tab=versions - Go yaml.v2 329 | https://pkg.go.dev/gopkg.in/yaml.v3@v3.0.0-20200313102051-9f266ea9e77c?tab=versions - Go yaml.v3 330 | https://pkg.go.dev/helm.sh/helm/v3@v3.5.3?tab=versions - Go helm 331 | https://pkg.go.dev/k8s.io/api@v0.20.5 - k8s api 332 | https://pkg.go.dev/k8s.io/apimachinery@v0.20.5?tab=versions - k8s apimachinery 333 | https://pkg.go.dev/k8s.io/client-go@v0.20.5?tab=versions - k8s client-go 334 | https://pkg.go.dev/github.com/docker/docker@v20.10.12+incompatible?tab=versions - Docker 335 | https://pkg.go.dev/github.com/docker/distribution - Docker distribution 336 | 337 | Note: End-users should recompile the code if there is a change in package version. You can follow the procedure shared in "https://freshman.tech/snippets/go/cross-compile-go-programs/" to package based on their hardware architecture and operating system. 338 | 339 | ## **Roadmap:** 340 | * Support for Custom Resource Definition(CRD) 341 | * Support for other cloud providers Kubernetes clusters 342 | * Support for on-prem Kubernetes clusters 343 | * Support for data migrations 344 | -------------------------------------------------------------------------------- /app/cluster/cluster.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: MIT-0 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | * software and associated documentation files (the "Software"), to deal in the Software 7 | * without restriction, including without limitation the rights to use, copy, modify, 8 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | 20 | package cluster 21 | 22 | import ( 23 | "os" 24 | "fmt" 25 | "k8s.io/client-go/kubernetes" 26 | "k8s.io/client-go/rest" 27 | "k8s.io/client-go/tools/clientcmd" 28 | ) 29 | 30 | // establish connection with ks8 31 | type Cluster struct { 32 | Kubeconfig_path string // Path to the kubeconfig file 33 | Clientset *kubernetes.Clientset // Client pointing the CKE cluster 34 | Region string // GCP region in which the cluster is running 35 | Namespaces []string // namespaces in kubernetes cluster from which the resources will be scanned 36 | Context string // context of Kubeconfig file 37 | Resources []string // Resources to include 38 | Helm_path string // Path to save helm path on local system 39 | Migrate_Images string // Migrate images from 3rd party registries to ECR 40 | Registry_Names []string // List of 3rd party registry names 41 | 42 | } 43 | 44 | func (c *Cluster) SetKubeconfig_path(kubeconfig_path string) { 45 | c.Kubeconfig_path = kubeconfig_path 46 | } 47 | 48 | func (c Cluster) GetKubeconfig_path() string { 49 | return c.Kubeconfig_path 50 | } 51 | 52 | func (c *Cluster) SetClientset(clientset *kubernetes.Clientset) { 53 | c.Clientset = clientset 54 | } 55 | 56 | func (c Cluster) GetClientset() *kubernetes.Clientset { 57 | return c.Clientset 58 | } 59 | 60 | func (c *Cluster) SetRegion(region string) { 61 | c.Region = region 62 | } 63 | 64 | func (c Cluster) GetRegion() string { 65 | return c.Region 66 | } 67 | 68 | func (c *Cluster) SetNamespaces(namespaces []string) { 69 | c.Namespaces = namespaces 70 | } 71 | 72 | func (c Cluster) GetNamespaces() []string { 73 | return c.Namespaces 74 | } 75 | 76 | func (c *Cluster) SetContext(context string) { 77 | c.Context = context 78 | } 79 | 80 | func (c Cluster) GetContext() string { 81 | return c.Context 82 | } 83 | 84 | func (c *Cluster) SetResources(resources []string) { 85 | c.Resources = resources 86 | } 87 | 88 | func (c Cluster) GetResources() []string { 89 | return c.Resources 90 | } 91 | 92 | func (c *Cluster) SetHelm_path(helm_path string) { 93 | c.Helm_path = helm_path 94 | } 95 | 96 | func (c Cluster) GetHelm_path() string { 97 | return c.Helm_path 98 | } 99 | 100 | func (c *Cluster) SetMigrate_Images(migrate_images string) { 101 | c.Migrate_Images = migrate_images 102 | } 103 | 104 | func (c Cluster) GetMigrate_Images() string { 105 | return c.Migrate_Images 106 | } 107 | 108 | func (c *Cluster) SetRegistry_Names(reg_names string) { 109 | c.Registry_Names = append(c.Registry_Names, reg_names) 110 | } 111 | 112 | func (c Cluster) GetRegistry_Names() []string { 113 | return c.Registry_Names 114 | } 115 | 116 | // retrieve cluster client 117 | func get_cluster_client(context, kubeconfigPath string) (*rest.Config, error) { 118 | return clientcmd.NewNonInteractiveDeferredLoadingClientConfig( 119 | &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfigPath}, 120 | &clientcmd.ConfigOverrides{ 121 | CurrentContext: context, 122 | }).ClientConfig() 123 | } 124 | 125 | // Generate client for the source cluster config passed 126 | func (c *Cluster) Generate_cluster_client() { 127 | //c.Clientset = clientset 128 | 129 | config, err := get_cluster_client(c.Context, c.Kubeconfig_path) 130 | if err != nil { 131 | fmt.Printf("The kubeconfig cannot be loaded: %v\n", err) 132 | os.Exit(1) 133 | } 134 | clientset, err := kubernetes.NewForConfig(config) 135 | c.SetClientset ( clientset ) 136 | } 137 | -------------------------------------------------------------------------------- /app/resource/resource.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: MIT-0 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | * software and associated documentation files (the "Software"), to deal in the Software 7 | * without restriction, including without limitation the rights to use, copy, modify, 8 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | 20 | package resource 21 | 22 | import ( 23 | 24 | app "k8s.io/api/apps/v1" 25 | autoscaling "k8s.io/api/autoscaling/v1" 26 | v1 "k8s.io/api/core/v1" 27 | networking "k8s.io/api/networking/v1" 28 | podsecuritypolicy "k8s.io/api/policy/v1beta1" 29 | rbac "k8s.io/api/rbac/v1" 30 | storage "k8s.io/api/storage/v1" 31 | 32 | admissionregistration "k8s.io/api/admissionregistration/v1" 33 | batchv1 "k8s.io/api/batch/v1" 34 | batchv1beta1 "k8s.io/api/batch/v1beta1" 35 | _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" 36 | ) 37 | 38 | type Resources struct { 39 | Svcl []v1.Service 40 | Nsl *v1.NamespaceList 41 | Dsl []app.DaemonSet 42 | SecretList []v1.Secret 43 | //var map[string] 44 | Depl []app.Deployment 45 | StorageClassList []storage.StorageClass 46 | ConfigMapsList []v1.ConfigMap 47 | IngressList []networking.Ingress 48 | RoleList []rbac.Role 49 | RoleBindingList []rbac.RoleBinding 50 | ClusterRoleList []rbac.ClusterRole 51 | ClusterRoleBindingList []rbac.ClusterRoleBinding 52 | HpaList []autoscaling.HorizontalPodAutoscaler 53 | PspList []podsecuritypolicy.PodSecurityPolicy 54 | SvcAccList []v1.ServiceAccount 55 | CronJobList []batchv1beta1.CronJob 56 | JobList []batchv1.Job 57 | PersistentVolumeClaimsList []v1.PersistentVolumeClaim 58 | MutatingWebhookConfigurationList []admissionregistration.MutatingWebhookConfiguration 59 | ValidatingWebhookConfigurationList []admissionregistration.ValidatingWebhookConfiguration 60 | HelmList map[string]map[string]string // Helm data namespace: [ release name : path to chart] 61 | // crdList *unstructured.UnstructuredList //[]apiextensions.CustomResourceDefinition 62 | } -------------------------------------------------------------------------------- /app/source/aks/aks.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: MIT-0 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | * software and associated documentation files (the "Software"), to deal in the Software 7 | * without restriction, including without limitation the rights to use, copy, modify, 8 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | package aks 20 | 21 | import ( 22 | cluster "containers-migration-factory/app/cluster" 23 | resource "containers-migration-factory/app/resource" 24 | source_impl "containers-migration-factory/app/source/source_impl" 25 | "fmt" 26 | ) 27 | 28 | // AKS defines as source 29 | type AKS struct{} 30 | 31 | var log = false 32 | 33 | func (c AKS) Connect(sCluster *cluster.Cluster) { 34 | sCluster.Generate_cluster_client() 35 | } 36 | 37 | func (g AKS) GetSourceDetails(sCluster *cluster.Cluster) resource.Resources { 38 | fmt.Println("AKS GetSourceDetails....") 39 | resources := resource.Resources{} 40 | source_impl.Generate_namespace_list(sCluster, &resources) 41 | 42 | source_impl.Generate_helm_charts(sCluster, &resources) 43 | 44 | source_impl.Generate_job_config(sCluster, &resources) 45 | 46 | source_impl.Generate_cronjob_config(sCluster, &resources) 47 | source_impl.Generate_secret_config(sCluster, &resources) 48 | source_impl.Generate_configmap_config(sCluster, &resources) 49 | //source_impl.Generate_mutatingwebhook_config(sCluster, &resources) 50 | //source_impl.Generate_validatingwebhook_config(sCluster, &resources) 51 | source_impl.Generate_ingress_config(sCluster, &resources) 52 | source_impl.Generate_storage_class_config(sCluster, &resources) 53 | source_impl.Generate_pvc_config(sCluster, &resources) 54 | source_impl.Generate_deployment_config(sCluster, &resources) 55 | 56 | source_impl.Generate_service_config(sCluster, &resources) 57 | source_impl.Generate_daemonset_config(sCluster, &resources) 58 | source_impl.Generate_hpa_config(sCluster, &resources) 59 | source_impl.Generate_psp_config(sCluster, &resources) 60 | source_impl.Generate_serviceaccount_config(sCluster, &resources) 61 | source_impl.Generate_role_config(sCluster, &resources) 62 | source_impl.Generate_role_binding_config(sCluster, &resources) 63 | source_impl.Generate_cluster_role_config(sCluster, &resources) 64 | source_impl.Generate_cluster_role_binding_config(sCluster, &resources) 65 | 66 | if log { 67 | fmt.Println("......JobList......", resources.JobList) 68 | fmt.Println("......Deployments......", resources.Depl) 69 | fmt.Println("......DaemonSet......", resources.Dsl) 70 | fmt.Println("......ServiceList......", resources.Svcl) 71 | fmt.Println("......StorageClassList......", resources.StorageClassList) 72 | fmt.Println("......ConfigMapsList......", resources.ConfigMapsList) 73 | fmt.Println("......IngressList......", resources.IngressList) 74 | fmt.Println("......RoleList......", resources.RoleList) 75 | fmt.Println("......RoleBindingList......", resources.RoleBindingList) 76 | fmt.Println("......ClusterRoleList......", resources.ClusterRoleList) 77 | fmt.Println("......ClusterRoleBindingList......", resources.ClusterRoleBindingList) 78 | fmt.Println("......HpaList......", resources.HpaList) 79 | fmt.Println("......PspList......", resources.PspList) 80 | fmt.Println("......SvcAccList......", resources.SvcAccList) 81 | fmt.Println("......CronJobList......", resources.CronJobList) 82 | fmt.Println("......PersistentVolumeClaimsList......", resources.PersistentVolumeClaimsList) 83 | //fmt.Println("......MutatingWebhookConfigurationList......", resources.MutatingWebhookConfigurationList) 84 | //fmt.Println("......ValidatingWebhookConfigurationList......", resources.ValidatingWebhookConfigurationList) 85 | fmt.Println("......HelmList......", resources.HelmList) 86 | fmt.Println("......JobList......", resources.JobList) 87 | } 88 | return resources 89 | } 90 | 91 | // AKS FormatSourceData implements the Geometry interface 92 | func (g AKS) FormatSourceData(resource *resource.Resources, resToInclude []string) { 93 | fmt.Println("AKS FormatSourceData....start") 94 | source_impl.Resource_trim_fields("Namespace", resource, resToInclude) 95 | source_impl.Resource_trim_fields("DaemonSet", resource, resToInclude) 96 | //source_impl.Resource_trim_fields("MutatingWebhookConfiguration", resource, resToInclude) 97 | source_impl.Resource_trim_fields("Deployment", resource, resToInclude) 98 | source_impl.Resource_trim_fields("Service", resource, resToInclude) 99 | source_impl.Resource_trim_fields("Secrets", resource, resToInclude) 100 | source_impl.Resource_trim_fields("StorageClasses", resource, resToInclude) 101 | source_impl.Resource_trim_fields("Roles", resource, resToInclude) 102 | source_impl.Resource_trim_fields("RoleBindings", resource, resToInclude) 103 | source_impl.Resource_trim_fields("ClusterRoles", resource, resToInclude) 104 | source_impl.Resource_trim_fields("ClusterRoleBindings", resource, resToInclude) 105 | source_impl.Resource_trim_fields("HorizontalPodAutoscaler", resource, resToInclude) 106 | source_impl.Resource_trim_fields("PodSecurityPolicy", resource, resToInclude) 107 | source_impl.Resource_trim_fields("ServiceAccount", resource, resToInclude) 108 | source_impl.Resource_trim_fields("PersistentVolumeClaim", resource, resToInclude) 109 | source_impl.Resource_trim_fields("CronJob", resource, resToInclude) 110 | source_impl.Resource_trim_fields("Job", resource, resToInclude) 111 | source_impl.Resource_trim_fields("ConfigMap", resource, resToInclude) 112 | source_impl.Resource_trim_fields("Ingress", resource, resToInclude) 113 | fmt.Println("AKS FormatSourceData....End") 114 | } 115 | -------------------------------------------------------------------------------- /app/source/gke/gke.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: MIT-0 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | * software and associated documentation files (the "Software"), to deal in the Software 7 | * without restriction, including without limitation the rights to use, copy, modify, 8 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | 20 | package gke 21 | 22 | import ( 23 | cluster "containers-migration-factory/app/cluster" 24 | resource "containers-migration-factory/app/resource" 25 | source_impl "containers-migration-factory/app/source/source_impl" 26 | "fmt" 27 | ) 28 | 29 | type GKE struct{} 30 | 31 | var log = false 32 | 33 | func (c GKE) Connect(sCluster *cluster.Cluster) { 34 | sCluster.Generate_cluster_client() 35 | } 36 | 37 | // GCP GetSourceDetails implements the Source interface 38 | func (g GKE) GetSourceDetails(sCluster *cluster.Cluster) resource.Resources { 39 | fmt.Println("GKE GetSourceDetails....") 40 | resources := resource.Resources{} 41 | source_impl.Generate_namespace_list(sCluster, &resources) 42 | 43 | source_impl.Generate_helm_charts(sCluster, &resources) 44 | 45 | source_impl.Generate_job_config(sCluster, &resources) 46 | 47 | source_impl.Generate_cronjob_config(sCluster, &resources) 48 | source_impl.Generate_secret_config(sCluster, &resources) 49 | source_impl.Generate_configmap_config(sCluster, &resources) 50 | source_impl.Generate_mutatingwebhook_config(sCluster, &resources) 51 | source_impl.Generate_validatingwebhook_config(sCluster, &resources) 52 | source_impl.Generate_ingress_config(sCluster, &resources) 53 | source_impl.Generate_storage_class_config(sCluster, &resources) 54 | source_impl.Generate_pvc_config(sCluster, &resources) 55 | source_impl.Generate_deployment_config(sCluster, &resources) 56 | 57 | source_impl.Generate_service_config(sCluster, &resources) 58 | source_impl.Generate_daemonset_config(sCluster, &resources) 59 | source_impl.Generate_hpa_config(sCluster, &resources) 60 | source_impl.Generate_psp_config(sCluster, &resources) 61 | source_impl.Generate_serviceaccount_config(sCluster, &resources) 62 | source_impl.Generate_role_config(sCluster, &resources) 63 | source_impl.Generate_role_binding_config(sCluster, &resources) 64 | source_impl.Generate_cluster_role_config(sCluster, &resources) 65 | source_impl.Generate_cluster_role_binding_config(sCluster, &resources) 66 | 67 | if log { 68 | fmt.Println("......JobList......", resources.JobList) 69 | fmt.Println("......Deployments......", resources.Depl) 70 | fmt.Println("......DaemonSet......", resources.Dsl) 71 | fmt.Println("......ServiceList......", resources.Svcl) 72 | fmt.Println("......StorageClassList......", resources.StorageClassList) 73 | fmt.Println("......ConfigMapsList......", resources.ConfigMapsList) 74 | fmt.Println("......IngressList......", resources.IngressList) 75 | fmt.Println("......RoleList......", resources.RoleList) 76 | fmt.Println("......RoleBindingList......", resources.RoleBindingList) 77 | fmt.Println("......ClusterRoleList......", resources.ClusterRoleList) 78 | fmt.Println("......ClusterRoleBindingList......", resources.ClusterRoleBindingList) 79 | fmt.Println("......HpaList......", resources.HpaList) 80 | fmt.Println("......PspList......", resources.PspList) 81 | fmt.Println("......SvcAccList......", resources.SvcAccList) 82 | fmt.Println("......CronJobList......", resources.CronJobList) 83 | fmt.Println("......PersistentVolumeClaimsList......", resources.PersistentVolumeClaimsList) 84 | fmt.Println("......MutatingWebhookConfigurationList......", resources.MutatingWebhookConfigurationList) 85 | fmt.Println("......ValidatingWebhookConfigurationList......", resources.ValidatingWebhookConfigurationList) 86 | fmt.Println("......HelmList......", resources.HelmList) 87 | fmt.Println("......JobList......", resources.JobList) 88 | } 89 | 90 | return resources 91 | 92 | } 93 | 94 | // GCP FormatSourceData implements the Geometry interface 95 | func (g GKE) FormatSourceData(resource *resource.Resources, resToInclude []string) { 96 | fmt.Println("GKE FormatSourceData....start") 97 | source_impl.Resource_trim_fields("Namespace", resource, resToInclude) 98 | source_impl.Resource_trim_fields("DaemonSet", resource, resToInclude) 99 | source_impl.Resource_trim_fields("MutatingWebhookConfiguration", resource, resToInclude) 100 | source_impl.Resource_trim_fields("Deployment", resource, resToInclude) 101 | source_impl.Resource_trim_fields("Service", resource, resToInclude) 102 | source_impl.Resource_trim_fields("Secrets", resource, resToInclude) 103 | source_impl.Resource_trim_fields("StorageClasses", resource, resToInclude) 104 | source_impl.Resource_trim_fields("Roles", resource, resToInclude) 105 | source_impl.Resource_trim_fields("RoleBindings", resource, resToInclude) 106 | source_impl.Resource_trim_fields("ClusterRoles", resource, resToInclude) 107 | source_impl.Resource_trim_fields("ClusterRoleBindings", resource, resToInclude) 108 | source_impl.Resource_trim_fields("HorizontalPodAutoscaler", resource, resToInclude) 109 | source_impl.Resource_trim_fields("PodSecurityPolicy", resource, resToInclude) 110 | source_impl.Resource_trim_fields("ServiceAccount", resource, resToInclude) 111 | source_impl.Resource_trim_fields("PersistentVolumeClaim", resource, resToInclude) 112 | source_impl.Resource_trim_fields("CronJob", resource, resToInclude) 113 | source_impl.Resource_trim_fields("Job", resource, resToInclude) 114 | source_impl.Resource_trim_fields("ConfigMap", resource, resToInclude) 115 | source_impl.Resource_trim_fields("Ingress", resource, resToInclude) 116 | fmt.Println("GKE FormatSourceData....End") 117 | 118 | } 119 | -------------------------------------------------------------------------------- /app/source/kops/kops.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: MIT-0 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | * software and associated documentation files (the "Software"), to deal in the Software 7 | * without restriction, including without limitation the rights to use, copy, modify, 8 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | 20 | package kops 21 | 22 | import ( 23 | cluster "containers-migration-factory/app/cluster" 24 | resource "containers-migration-factory/app/resource" 25 | "fmt" 26 | ) 27 | 28 | // kops defines as source 29 | type KOPS struct{} 30 | 31 | func (c KOPS) Connect(sCluster *cluster.Cluster) { 32 | sCluster.Generate_cluster_client() 33 | } 34 | 35 | func (k KOPS) GetSourceDetails(sCluster *cluster.Cluster) resource.Resources { 36 | fmt.Println("KOPS GetSourceDetails....") 37 | resources := resource.Resources{} 38 | 39 | return resources 40 | } 41 | 42 | // GCP FormatSourceData implements the Geometry interface 43 | func (k KOPS) FormatSourceData(resource *resource.Resources, resToInclude []string) { 44 | } 45 | -------------------------------------------------------------------------------- /app/source/source.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: MIT-0 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | * software and associated documentation files (the "Software"), to deal in the Software 7 | * without restriction, including without limitation the rights to use, copy, modify, 8 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | 20 | package source 21 | 22 | import ( 23 | // "fmt" 24 | cluster "containers-migration-factory/app/cluster" 25 | resource "containers-migration-factory/app/resource" 26 | ) 27 | 28 | // Geometry is an interface that defines Geometrical Calculation 29 | type Source interface { 30 | Connect(sCluster *cluster.Cluster) 31 | GetSourceDetails(sCluster *cluster.Cluster) resource.Resources 32 | FormatSourceData(resource *resource.Resources, resToInclude []string) // trim / data clean up 33 | } 34 | 35 | func SetContext(source Source,sCluster *cluster.Cluster){ 36 | /*Connect to source cluster*/ 37 | source.Connect(sCluster) 38 | } 39 | 40 | // Invoke specific source based on input provided 41 | func Invoke(source Source, sType string, sCluster *cluster.Cluster, dCluster *cluster.Cluster) resource.Resources { 42 | 43 | /*Get Source Details*/ 44 | 45 | resources := source.GetSourceDetails(sCluster) 46 | 47 | source.FormatSourceData(&resources, sCluster.Resources) 48 | 49 | return resources 50 | } 51 | -------------------------------------------------------------------------------- /app/source/source_impl/source_impl.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: MIT-0 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | * software and associated documentation files (the "Software"), to deal in the Software 7 | * without restriction, including without limitation the rights to use, copy, modify, 8 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | 20 | package source_impl 21 | 22 | import ( 23 | "context" 24 | "fmt" 25 | "os" 26 | "strings" 27 | 28 | "bytes" 29 | "compress/gzip" 30 | "encoding/base64" 31 | "encoding/json" 32 | "io" 33 | "io/ioutil" 34 | "log" 35 | "path/filepath" 36 | 37 | yaml "github.com/ghodss/yaml" 38 | helm "helm.sh/helm/v3/pkg/release" 39 | app "k8s.io/api/apps/v1" 40 | autoscaling "k8s.io/api/autoscaling/v1" 41 | v1 "k8s.io/api/core/v1" 42 | podsecuritypolicy "k8s.io/api/policy/v1beta1" 43 | rbac "k8s.io/api/rbac/v1" 44 | storage "k8s.io/api/storage/v1" 45 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 46 | networking "k8s.io/api/networking/v1" 47 | 48 | admissionregistration "k8s.io/api/admissionregistration/v1" 49 | batchv1 "k8s.io/api/batch/v1" 50 | batchv1beta1 "k8s.io/api/batch/v1beta1" 51 | 52 | _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" 53 | 54 | cluster "containers-migration-factory/app/cluster" 55 | resource "containers-migration-factory/app/resource" 56 | MIGRATE_IMAGES "containers-migration-factory/controllers/MIGRATE_IMAGES" 57 | ) 58 | 59 | //TODO: remove after all function migrated 60 | 61 | var err error 62 | 63 | var ignore_ds = map[string][]string{"kube-system": {"fluentd-gke", "gke-metrics-agent", "gke-metrics-agent-windows", "kube-proxy", "metadata-proxy-v0.1", "nvidia-gpu-device-plugin", "prometheus-to-sd"}} 64 | var ignore_svc = map[string][]string{"kube-system": {"default-http-backend", "kube-dns", "metrics-server"}, "default": {"kubernetes"}} 65 | var ignore_dep = map[string][]string{"kube-system": {"event-exporter-gke", "fluentd-gke-scaler", "kube-dns", "kube-dns-autoscaler", "l7-default-backend", "metrics-server-v0.3.6", "stackdriver-metadata-agent-cluster-level"}} 66 | 67 | func stringInSlice(a string, list []string) bool { 68 | for _, b := range list { 69 | if strings.ToLower(b) == a { 70 | return true 71 | } 72 | } 73 | return false 74 | } 75 | 76 | func itemExists(a []string, list []string) bool { 77 | for _, b := range list { 78 | for _, c := range a { 79 | if strings.ToLower(b) == strings.ToLower(c) { 80 | return true 81 | } 82 | } 83 | } 84 | return false 85 | } 86 | 87 | func trimCommonKeys(a string, list []string) bool { 88 | for _, b := range list { 89 | if strings.ToLower(b) == a { 90 | return true 91 | } 92 | } 93 | return false 94 | } 95 | 96 | func Trim_Item(ObjectMeta *metav1.ObjectMeta) { 97 | Trim_Item_All(ObjectMeta, true) 98 | } 99 | 100 | func Trim_Item_All(ObjectMeta *metav1.ObjectMeta, delGenAnnotation bool) { 101 | ObjectMeta.SelfLink = "" 102 | ObjectMeta.UID = "" 103 | ObjectMeta.ResourceVersion = "" 104 | ObjectMeta.Generation = 0 105 | ObjectMeta.CreationTimestamp = metav1.Time{} 106 | if delGenAnnotation { 107 | delete(ObjectMeta.Annotations, "deprecated.daemonset.template.generation") 108 | } 109 | delete(ObjectMeta.Annotations, "kubectl.kubernetes.io/last-applied-configuration") 110 | 111 | } 112 | 113 | // Trim the unrequired fields from resource configuration 114 | func Resource_trim_fields(resource_type string, resource *resource.Resources, resToInclude []string) { 115 | 116 | if resource_type == "Namespace" { 117 | var resource_list []v1.Namespace 118 | for _, item := range resource.Nsl.Items { 119 | Trim_Item(&item.ObjectMeta) 120 | resource_list = append(resource_list, item) 121 | } 122 | resource.Nsl.Items = resource_list 123 | } 124 | 125 | if resource_type == "MutatingWebhookConfiguration" && itemExists([]string{"MutatingWebhookConfigurations", "MutatingWebhookConfiguration", "all"}, resToInclude) { 126 | var resource_list []admissionregistration.MutatingWebhookConfiguration 127 | for _, item := range resource.MutatingWebhookConfigurationList { 128 | Trim_Item(&item.ObjectMeta) 129 | resource_list = append(resource_list, item) 130 | } 131 | resource.MutatingWebhookConfigurationList = resource_list 132 | } 133 | 134 | if resource_type == "Ingress" && itemExists([]string{"Ingress", "Ingresses", "all"}, resToInclude) { 135 | var resource_list []networking.Ingress 136 | for _, item := range resource.IngressList { 137 | Trim_Item(&item.ObjectMeta) 138 | resource_list = append(resource_list, item) 139 | } 140 | resource.IngressList = resource_list 141 | } 142 | 143 | if resource_type == "DaemonSet" && itemExists([]string{"daemonset", "daemonsets", "ds", "all"}, resToInclude) { 144 | var resource_list []app.DaemonSet 145 | for _, item := range resource.Dsl { 146 | Trim_Item(&item.ObjectMeta) 147 | resource_list = append(resource_list, item) 148 | } 149 | resource.Dsl = resource_list 150 | } 151 | 152 | if resource_type == "Service" && itemExists([]string{"service", "svc", "all"}, resToInclude) { 153 | var resource_list []v1.Service 154 | for _, item := range resource.Svcl { 155 | item.ObjectMeta.SelfLink = "" 156 | item.ObjectMeta.UID = "" 157 | item.ResourceVersion = "" 158 | item.Generation = 0 159 | item.CreationTimestamp = metav1.Time{} 160 | item.Spec.ClusterIP = "" 161 | item.Spec.ClusterIPs = nil 162 | for port, _ := range item.Spec.Ports { 163 | item.Spec.Ports[port].NodePort = 0 164 | } 165 | delete(item.ObjectMeta.Annotations, "deprecated.daemonset.template.generation") 166 | delete(item.ObjectMeta.Annotations, "kubectl.kubernetes.io/last-applied-configuration") 167 | resource_list = append(resource_list, item) 168 | } 169 | resource.Svcl = resource_list 170 | } 171 | 172 | if resource_type == "Deployment" && itemExists([]string{"deployment", "deployments", "deploy", "all"}, resToInclude) { 173 | var resource_list []app.Deployment 174 | for _, item := range resource.Depl { 175 | Trim_Item(&item.ObjectMeta) 176 | resource_list = append(resource_list, item) 177 | } 178 | resource.Depl = resource_list 179 | } 180 | 181 | if resource_type == "Secrets" && itemExists([]string{"secrets", "secret", "all"}, resToInclude) { 182 | var resource_list []v1.Secret 183 | for _, item := range resource.SecretList { 184 | Trim_Item(&item.ObjectMeta) 185 | resource_list = append(resource_list, item) 186 | } 187 | resource.SecretList = resource_list 188 | } 189 | 190 | if resource_type == "StorageClasses" && itemExists([]string{"storageclasses", "storageclass", "sc", "all"}, resToInclude) { 191 | var resource_list []storage.StorageClass 192 | for _, item := range resource.StorageClassList { 193 | Trim_Item(&item.ObjectMeta) 194 | resource_list = append(resource_list, item) 195 | } 196 | resource.StorageClassList = resource_list 197 | } 198 | 199 | if resource_type == "ConfigMap" && itemExists([]string{"configmap", "configmaps", "cm", "all"}, resToInclude) { 200 | var resource_list []v1.ConfigMap 201 | for _, item := range resource.ConfigMapsList { 202 | Trim_Item(&item.ObjectMeta) 203 | resource_list = append(resource_list, item) 204 | } 205 | resource.ConfigMapsList = resource_list 206 | } 207 | 208 | if resource_type == "Roles" && itemExists([]string{"role", "roles", "all"}, resToInclude) { 209 | var resource_list []rbac.Role 210 | for _, item := range resource.RoleList { 211 | Trim_Item(&item.ObjectMeta) 212 | resource_list = append(resource_list, item) 213 | } 214 | resource.RoleList = resource_list 215 | } 216 | if resource_type == "PersistentVolumeClaim" && itemExists([]string{"persistentvolumeclaims", "persistentvolumeclaim", "pvc", "all"}, resToInclude) { 217 | var resource_list []v1.PersistentVolumeClaim 218 | for _, item := range resource.PersistentVolumeClaimsList { 219 | Trim_Item(&item.ObjectMeta) 220 | resource_list = append(resource_list, item) 221 | } 222 | resource.PersistentVolumeClaimsList = resource_list 223 | } 224 | 225 | if resource_type == "CronJob" && itemExists([]string{"cronjobs", "cronjob", "cj", "all"}, resToInclude) { 226 | var resource_list []batchv1beta1.CronJob 227 | for _, item := range resource.CronJobList { 228 | Trim_Item(&item.ObjectMeta) 229 | resource_list = append(resource_list, item) 230 | } 231 | resource.CronJobList = resource_list 232 | } 233 | 234 | if resource_type == "Job" && itemExists([]string{"jobs", "job", "all"}, resToInclude) { 235 | var resource_list []batchv1.Job 236 | for _, item := range resource.JobList { 237 | Trim_Item(&item.ObjectMeta) 238 | delete(item.Spec.Selector.MatchLabels,"controller-uid") 239 | delete(item.Spec.Template.Labels, "controller-uid") 240 | resource_list = append(resource_list, item) 241 | } 242 | resource.JobList = resource_list 243 | } 244 | 245 | if resource_type == "ValidatingWebhookConfiguration" && itemExists([]string{"validatingwebhookconfiguration", "validatingwebhookconfigurations", "all"}, resToInclude) { 246 | var resource_list []admissionregistration.ValidatingWebhookConfiguration 247 | for _, item := range resource.ValidatingWebhookConfigurationList { 248 | Trim_Item(&item.ObjectMeta) 249 | resource_list = append(resource_list, item) 250 | } 251 | resource.ValidatingWebhookConfigurationList = resource_list 252 | } 253 | 254 | if resource_type == "RoleBindings" && itemExists([]string{"rolebinding", "rolebindings", "all"}, resToInclude) { 255 | var resource_list []rbac.RoleBinding 256 | for _, item := range resource.RoleBindingList { 257 | Trim_Item(&item.ObjectMeta) 258 | resource_list = append(resource_list, item) 259 | } 260 | resource.RoleBindingList = resource_list 261 | } 262 | 263 | if resource_type == "ClusterRoles" && itemExists([]string{"clusterrole", "clusterroles", "all"}, resToInclude) { 264 | var resource_list []rbac.ClusterRole 265 | for _, item := range resource.ClusterRoleList { 266 | Trim_Item(&item.ObjectMeta) 267 | resource_list = append(resource_list, item) 268 | } 269 | resource.ClusterRoleList = resource_list 270 | } 271 | 272 | if resource_type == "ClusterRoleBindings" && itemExists([]string{"clusterrolebinding", "clusterrolebindings", "all"}, resToInclude) { 273 | var resource_list []rbac.ClusterRoleBinding 274 | for _, item := range resource.ClusterRoleBindingList { 275 | Trim_Item(&item.ObjectMeta) 276 | resource_list = append(resource_list, item) 277 | } 278 | resource.ClusterRoleBindingList = resource_list 279 | } 280 | 281 | if resource_type == "HorizontalPodAutoscaler" && itemExists([]string{"horizontalpodautoscaler", "horizontalpodautoscalers"}, resToInclude) { 282 | var resource_list []autoscaling.HorizontalPodAutoscaler 283 | for _, item := range resource.HpaList { 284 | Trim_Item_All(&item.ObjectMeta, false) 285 | resource_list = append(resource_list, item) 286 | } 287 | resource.HpaList = resource_list 288 | } 289 | 290 | if resource_type == "PodSecurityPolicy" && itemExists([]string{"podsecuritypolicies", "podsecuritypolicy", "psp", "all"}, resToInclude) { 291 | var resource_list []podsecuritypolicy.PodSecurityPolicy 292 | for _, item := range resource.PspList { 293 | Trim_Item_All(&item.ObjectMeta, false) 294 | resource_list = append(resource_list, item) 295 | } 296 | resource.PspList = resource_list 297 | } 298 | 299 | if resource_type == "ServiceAccount" && itemExists([]string{"serviceaccount", "serviceaccounts", "sa", "all"}, resToInclude) { 300 | var resource_list []v1.ServiceAccount 301 | for _, item := range resource.SvcAccList { 302 | Trim_Item_All(&item.ObjectMeta, false) 303 | resource_list = append(resource_list, item) 304 | } 305 | resource.SvcAccList = resource_list 306 | } 307 | } 308 | 309 | // Scan source kubernetes cluster and generate the Job objects 310 | func Generate_job_config(src *cluster.Cluster, resource *resource.Resources) { 311 | if stringInSlice("jobs", src.GetResources()) || stringInSlice("job", src.GetResources()) || stringInSlice("all", src.GetResources()) { 312 | // Loop through all the namespaces and get the list of services 313 | for _, element := range resource.Nsl.Items { 314 | job, err := src.GetClientset().BatchV1().Jobs(element.ObjectMeta.Name).List(context.TODO(), metav1.ListOptions{}) 315 | if err != nil { 316 | fmt.Printf("Could not read kubernetes Secrets using cluster client: %v\n", err) 317 | os.Exit(1) 318 | } 319 | 320 | // append list of services in this namespace to glabal services list 321 | resource.JobList = append(resource.JobList, job.Items...) 322 | } 323 | } 324 | } 325 | 326 | // Scan source kubernetes cluster and generate the CronJob objects 327 | func Generate_cronjob_config(src *cluster.Cluster, resource *resource.Resources) { 328 | if stringInSlice("cronjobs", src.GetResources()) || stringInSlice("cronjob", src.GetResources()) || stringInSlice("cj", src.GetResources()) || stringInSlice("all", src.GetResources()) { 329 | // Loop through all the namespaces and get the list of services 330 | for _, element := range resource.Nsl.Items { 331 | cronjob, err := src.GetClientset().BatchV1beta1().CronJobs(element.ObjectMeta.Name).List(context.TODO(), metav1.ListOptions{}) 332 | if err != nil { 333 | fmt.Printf("Could not read kubernetes Secrets using cluster client: %v\n", err) 334 | os.Exit(1) 335 | } 336 | 337 | if src.Migrate_Images == "Yes" || src.Migrate_Images == "yes" { 338 | for i, item := range cronjob.Items { 339 | for j, image_spec := range item.Spec.JobTemplate.Spec.Template.Spec.Containers { 340 | image_name := image_spec.Image 341 | updated_image := MIGRATE_IMAGES.Validate(image_name, src.Registry_Names) 342 | if updated_image != "" { 343 | cronjob.Items[i].Spec.JobTemplate.Spec.Template.Spec.Containers[j].Image = updated_image 344 | } 345 | } 346 | } 347 | } 348 | 349 | // append list of services in this namespace to glabal services list 350 | resource.CronJobList = append(resource.CronJobList, cronjob.Items...) 351 | } 352 | } 353 | } 354 | 355 | // Scan source kubernetes cluster and generate the secret objects 356 | func Generate_secret_config(src *cluster.Cluster, resource *resource.Resources) { 357 | if stringInSlice("secrets", src.GetResources()) || stringInSlice("secret", src.GetResources()) || stringInSlice("all", src.GetResources()) { 358 | // Loop through all the namespaces and get the list of services 359 | for _, element := range resource.Nsl.Items { 360 | secret, err := src.GetClientset().CoreV1().Secrets(element.ObjectMeta.Name).List(context.TODO(), metav1.ListOptions{}) 361 | if err != nil { 362 | fmt.Printf("Could not read kubernetes Secrets using cluster client: %v\n", err) 363 | os.Exit(1) 364 | } 365 | 366 | // Remove default secret from each namespace 367 | for i, item := range secret.Items { 368 | // if strings.Contains(item.ObjectMeta.Name, "default-token-") { 369 | if item.ObjectMeta.Annotations["kubernetes.io/service-account.name"] == "default" { 370 | secret.Items = append(secret.Items[:i], secret.Items[i+1:]...) 371 | } 372 | } 373 | 374 | // append list of services in this namespace to global services list 375 | resource.SecretList = append(resource.SecretList, secret.Items...) 376 | } 377 | } 378 | } 379 | 380 | // Scan source kubernetes cluster and generate the ConfigMap objects 381 | func Generate_configmap_config(src *cluster.Cluster, resource *resource.Resources) { 382 | if stringInSlice("configmaps", src.GetResources()) || stringInSlice("configmap", src.GetResources()) || stringInSlice("cm", src.GetResources()) || stringInSlice("all", src.GetResources()) { 383 | // Loop through all the namespaces and get the list of services 384 | for _, element := range resource.Nsl.Items { 385 | configmap, err := src.GetClientset().CoreV1().ConfigMaps(element.ObjectMeta.Name).List(context.TODO(), metav1.ListOptions{}) 386 | if err != nil { 387 | fmt.Printf("Could not read kubernetes ConfigMaps using cluster client: %v\n", err) 388 | os.Exit(1) 389 | } 390 | 391 | // append list of services in this namespace to global services list 392 | resource.ConfigMapsList = append(resource.ConfigMapsList, configmap.Items...) 393 | } 394 | } 395 | } 396 | 397 | // Scan source kubernetes cluster and generate the MutatingWebhookConfiguration objects 398 | func Generate_mutatingwebhook_config(src *cluster.Cluster, resource *resource.Resources) { 399 | if stringInSlice("mutatingWebhookconfigurations", src.GetResources()) || stringInSlice("mutatingwebhookconfiguration", src.GetResources()) || stringInSlice("all", src.GetResources()) { 400 | // Loop through all the namespaces and get the list of services 401 | mwc, err := src.GetClientset().AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.TODO(), metav1.ListOptions{}) 402 | if err != nil { 403 | fmt.Printf("Could not read kubernetes MutatingWebhookConfiguration using cluster client: %v\n", err) 404 | os.Exit(1) 405 | } 406 | 407 | // append list of services in this namespace to glabal services list 408 | resource.MutatingWebhookConfigurationList = append(resource.MutatingWebhookConfigurationList, mwc.Items...) 409 | } 410 | } 411 | 412 | // Scan source kubernetes cluster and generate the ValidtingWebhookConfiguration objects 413 | func Generate_validatingwebhook_config(src *cluster.Cluster, resource *resource.Resources) { 414 | if stringInSlice("validatingwebhookconfiguration", src.GetResources()) || stringInSlice("validatingwebhookconfigurations", src.GetResources()) || stringInSlice("all", src.GetResources()) { 415 | // Loop through all the namespaces and get the list of services 416 | vwc, err := src.GetClientset().AdmissionregistrationV1().ValidatingWebhookConfigurations().List(context.TODO(), metav1.ListOptions{}) 417 | if err != nil { 418 | fmt.Printf("Could not read kubernetes MutatingWebhookConfiguration using cluster client: %v\n", err) 419 | os.Exit(1) 420 | } 421 | 422 | // append list of services in this namespace to glabal services list 423 | resource.ValidatingWebhookConfigurationList = append(resource.ValidatingWebhookConfigurationList, vwc.Items...) 424 | } 425 | } 426 | 427 | // Scan source kubernetes cluster and generate the ConfigMap objects 428 | func Generate_ingress_config(src *cluster.Cluster, resource *resource.Resources) { 429 | if stringInSlice("ingresses", src.GetResources()) || stringInSlice("ingress", src.GetResources()) || stringInSlice("ing", src.GetResources()) || stringInSlice("all", src.GetResources()) { 430 | // Loop through all the namespaces and get the list of services 431 | for _, element := range resource.Nsl.Items { 432 | ingress, err := src.GetClientset().NetworkingV1().Ingresses(element.ObjectMeta.Name).List(context.TODO(), metav1.ListOptions{}) 433 | if err != nil { 434 | fmt.Printf("Could not read kubernetes Ingresses using cluster client: %v\n", err) 435 | //os.Exit(1) 436 | } 437 | 438 | // append list of services in this namespace to global services list 439 | resource.IngressList = append(resource.IngressList, ingress.Items...) 440 | } 441 | } 442 | } 443 | 444 | // Scan source kubernetes cluster and generate the Storage Class objects 445 | func Generate_storage_class_config(src *cluster.Cluster, resource *resource.Resources) { 446 | if stringInSlice("storageclasses", src.GetResources()) || stringInSlice("storageclass", src.GetResources()) || stringInSlice("sc", src.GetResources()) || stringInSlice("all", src.GetResources()) { 447 | // Loop through all the namespaces and get the list of services 448 | //for _, element := range resource.Nsl.Items { 449 | sc, err := src.GetClientset().StorageV1().StorageClasses().List(context.TODO(), metav1.ListOptions{}) 450 | if err != nil { 451 | fmt.Printf("Could not read kubernetes Storage Classes using cluster client: %v\n", err) 452 | os.Exit(1) 453 | } 454 | 455 | // append list of services in this namespace to global services list 456 | resource.StorageClassList = append(resource.StorageClassList, sc.Items...) 457 | //} 458 | } 459 | } 460 | 461 | // Scan source kubernetes cluster and generate the Persistent volume claim objects 462 | func Generate_pvc_config(src *cluster.Cluster, resource *resource.Resources) { 463 | if stringInSlice("persistentvolumeclaims", src.GetResources()) || stringInSlice("persistentvolumeclaim", src.GetResources()) || stringInSlice("pvc", src.GetResources()) || stringInSlice("all", src.GetResources()) { 464 | // Loop through all the namespaces and get the list of services 465 | for _, element := range resource.Nsl.Items { 466 | pvc, err := src.GetClientset().CoreV1().PersistentVolumeClaims(element.ObjectMeta.Name).List(context.TODO(), metav1.ListOptions{}) 467 | if err != nil { 468 | fmt.Printf("Could not read kubernetes Storage Classes using cluster client: %v\n", err) 469 | os.Exit(1) 470 | } 471 | 472 | // append list of services in this namespace to glabal services list 473 | resource.PersistentVolumeClaimsList = append(resource.PersistentVolumeClaimsList, pvc.Items...) 474 | } 475 | } 476 | } 477 | 478 | // Scan source kubernetes cluster and generate the service objects 479 | func Generate_deployment_config(src *cluster.Cluster, resource *resource.Resources) { 480 | if stringInSlice("deployment", src.GetResources()) || stringInSlice("deployments", src.GetResources()) || stringInSlice("deploy", src.GetResources()) || stringInSlice("all", src.GetResources()) { 481 | // Loop through all the namespaces and get the list of services 482 | for _, element := range resource.Nsl.Items { 483 | dep, err := src.GetClientset().AppsV1().Deployments(element.ObjectMeta.Name).List(context.TODO(), metav1.ListOptions{}) 484 | if err != nil { 485 | fmt.Printf("Could not read kubernetes SVC using cluster client: %v\n", err) 486 | os.Exit(1) 487 | } 488 | 489 | if src.Migrate_Images == "Yes" || src.Migrate_Images == "yes" { 490 | for i, item := range dep.Items { 491 | for j, image_spec := range item.Spec.Template.Spec.Containers { 492 | image_name := image_spec.Image 493 | updated_image := MIGRATE_IMAGES.Validate(image_name, src.Registry_Names) 494 | if updated_image != "" { 495 | dep.Items[i].Spec.Template.Spec.Containers[j].Image = updated_image 496 | } 497 | } 498 | } 499 | } 500 | 501 | // append list of services in this namespace to global services list 502 | resource.Depl = append(resource.Depl, dep.Items...) 503 | } 504 | } 505 | } 506 | 507 | // Scan source kubernetes cluster and generate the service objects 508 | func Generate_service_config(src *cluster.Cluster, resource *resource.Resources) { 509 | if stringInSlice("service", src.GetResources()) || stringInSlice("svc", src.GetResources()) || stringInSlice("all", src.GetResources()) { 510 | // Loop through all the namespaces and get the list of services 511 | for _, element := range resource.Nsl.Items { 512 | svc, err := src.GetClientset().CoreV1().Services(element.ObjectMeta.Name).List(context.TODO(), metav1.ListOptions{}) 513 | if err != nil { 514 | fmt.Printf("Could not read kubernetes SVC using cluster client: %v\n", err) 515 | os.Exit(1) 516 | } 517 | 518 | // append list of services in this namespace to global services list 519 | resource.Svcl = append(resource.Svcl, svc.Items...) 520 | } 521 | } 522 | } 523 | 524 | // Scan source kubernetes cluster and generate the daemonset objects 525 | func Generate_daemonset_config(src *cluster.Cluster, resource *resource.Resources) { 526 | if stringInSlice("daemonset", src.GetResources()) || stringInSlice("daemonsets", src.GetResources()) || stringInSlice("ds", src.GetResources()) || stringInSlice("all", src.GetResources()) { 527 | // Loop through all the namespaces and get the list of daemonsets 528 | for _, element := range resource.Nsl.Items { 529 | ds, err := src.GetClientset().AppsV1().DaemonSets(element.ObjectMeta.Name).List(context.TODO(), metav1.ListOptions{}) 530 | if err != nil { 531 | fmt.Printf("Could not read kubernetes Daemonsets using cluster client: %v\n", err) 532 | os.Exit(1) 533 | } 534 | 535 | // append list of services in this namespace to global services list 536 | resource.Dsl = append(resource.Dsl, ds.Items...) 537 | } 538 | } 539 | } 540 | 541 | // Scan source kubernetes cluster and generate the HPA objects 542 | func Generate_hpa_config(src *cluster.Cluster, resource *resource.Resources) { 543 | if stringInSlice("horizontalpodautoscaler", src.GetResources()) || stringInSlice("horizontalpodautoscalers", src.GetResources()) || stringInSlice("hpa", src.GetResources()) || stringInSlice("all", src.GetResources()) { 544 | // Loop through all the namespaces and get the list of hpas 545 | for _, element := range resource.Nsl.Items { 546 | hpa, err := src.GetClientset().AutoscalingV1().HorizontalPodAutoscalers(element.ObjectMeta.Name).List(context.TODO(), metav1.ListOptions{}) 547 | fmt.Println() 548 | if err != nil { 549 | fmt.Printf("Could not read kubernetes hpas using cluster client: %v\n", err) 550 | os.Exit(1) 551 | } 552 | 553 | // append list of services in this namespace to glabal services list 554 | resource.HpaList = append(resource.HpaList, hpa.Items...) 555 | } 556 | } 557 | } 558 | 559 | func Generate_psp_config(src *cluster.Cluster, resource *resource.Resources) { 560 | if stringInSlice("podsecuritypolicies", src.GetResources()) || stringInSlice("podsecuritypolicy", src.GetResources()) || stringInSlice("psp", src.GetResources()) || stringInSlice("all", src.GetResources()) { 561 | // Get the list of pod security policies 562 | psp, err := src.GetClientset().PolicyV1beta1().PodSecurityPolicies().List(context.TODO(), metav1.ListOptions{}) 563 | if err != nil { 564 | fmt.Printf("Could not read kubernetes pod security policies using cluster client: %v\n", err) 565 | os.Exit(1) 566 | } 567 | 568 | // append list of pod security policies to glabal services list 569 | resource.PspList = append(resource.PspList, psp.Items...) 570 | } 571 | } 572 | 573 | func Generate_serviceaccount_config(src *cluster.Cluster, resource *resource.Resources) { 574 | if stringInSlice("serviceaccount", src.GetResources()) || stringInSlice("serviceaccounts", src.GetResources()) || stringInSlice("sa", src.GetResources()) || stringInSlice("all", src.GetResources()) { 575 | // Loop through all the namespaces and get the list of daemonsets 576 | for _, element := range resource.Nsl.Items { 577 | sa, err := src.GetClientset().CoreV1().ServiceAccounts(element.ObjectMeta.Name).List(context.TODO(), metav1.ListOptions{}) 578 | if err != nil { 579 | fmt.Printf("Could not read kubernetes hpas using cluster client: %v\n", err) 580 | os.Exit(1) 581 | } 582 | 583 | // append list of service accounts in this namespace to glabal services list 584 | resource.SvcAccList = append(resource.SvcAccList, sa.Items...) 585 | } 586 | } 587 | } 588 | 589 | // Scan source kubernetes cluster and generate the role objects 590 | func Generate_role_config(src *cluster.Cluster, resource *resource.Resources) { 591 | if stringInSlice("role", src.GetResources()) || stringInSlice("roles", src.GetResources()) || stringInSlice("all", src.GetResources()) { 592 | // Loop through all the namespaces and get the list of daemonsets 593 | for _, element := range resource.Nsl.Items { 594 | rl, err := src.GetClientset().RbacV1().Roles(element.ObjectMeta.Name).List(context.TODO(), metav1.ListOptions{}) 595 | if err != nil { 596 | fmt.Printf("Could not read kubernetes Role using cluster client: %v\n", err) 597 | os.Exit(1) 598 | } 599 | 600 | // append list of services in this namespace to global services list 601 | resource.RoleList = append(resource.RoleList, rl.Items...) 602 | } 603 | } 604 | } 605 | 606 | // Scan source kubernetes cluster and generate the role binding objects 607 | func Generate_role_binding_config(src *cluster.Cluster, resource *resource.Resources) { 608 | if stringInSlice("rolebinding", src.GetResources()) || stringInSlice("rolebindings", src.GetResources()) || stringInSlice("all", src.GetResources()) { 609 | // Loop through all the namespaces and get the list of daemonsets 610 | for _, element := range resource.Nsl.Items { 611 | rbl, err := src.GetClientset().RbacV1().RoleBindings(element.ObjectMeta.Name).List(context.TODO(), metav1.ListOptions{}) 612 | if err != nil { 613 | fmt.Printf("Could not read kubernetes Role Bindings using cluster client: %v\n", err) 614 | os.Exit(1) 615 | } 616 | 617 | // append list of services in this namespace to global services list 618 | resource.RoleBindingList = append(resource.RoleBindingList, rbl.Items...) 619 | } 620 | } 621 | } 622 | 623 | // Scan source kubernetes cluster and generate the cluster role objects 624 | func Generate_cluster_role_config(src *cluster.Cluster, resource *resource.Resources) { 625 | if stringInSlice("clusterrole", src.GetResources()) || stringInSlice("clusterroles", src.GetResources()) || stringInSlice("all", src.GetResources()) { 626 | // not a namespaced resource and hence no loop through all the namespaces and get the list of clusterroles 627 | crl, err := src.GetClientset().RbacV1().ClusterRoles().List(context.TODO(), metav1.ListOptions{}) 628 | if err != nil { 629 | fmt.Printf("Could not read kubernetes Role Bindings using cluster client: %v\n", err) 630 | os.Exit(1) 631 | } 632 | 633 | // append list of services in this namespace to global services list 634 | resource.ClusterRoleList = append(resource.ClusterRoleList, crl.Items...) 635 | // } 636 | } 637 | } 638 | 639 | // Scan source kubernetes cluster and generate the cluster role objects 640 | func Generate_cluster_role_binding_config(src *cluster.Cluster, resource *resource.Resources) { 641 | if stringInSlice("clusterrolebinding", src.GetResources()) || stringInSlice("clusterrolebindings", src.GetResources()) || stringInSlice("all", src.GetResources()) { 642 | // not a namespaced resource and hence no loop through all the namespaces and get the list of clusterrole bindings 643 | crbl, err := src.GetClientset().RbacV1().ClusterRoleBindings().List(context.TODO(), metav1.ListOptions{}) 644 | if err != nil { 645 | fmt.Printf("Could not read kubernetes Role Bindings using cluster client: %v\n", err) 646 | os.Exit(1) 647 | } 648 | 649 | // append list of services in this namespace to global services list 650 | resource.ClusterRoleBindingList = append(resource.ClusterRoleBindingList, crbl.Items...) 651 | // } 652 | } 653 | } 654 | 655 | func Generate_namespace_list(src *cluster.Cluster, resource *resource.Resources) { 656 | if src.GetNamespaces() != nil && !(stringInSlice("all", src.GetNamespaces())) || (stringInSlice("all", src.GetNamespaces())) || (stringInSlice("namespaces", src.GetNamespaces())) || (stringInSlice("namespace", src.GetNamespaces())) || (stringInSlice("ns", src.GetNamespaces())) { 657 | // Intialize namespace list variable 658 | resource.Nsl = new(v1.NamespaceList) 659 | fmt.Println("Namespace list entered by user") 660 | 661 | //Loop through the list of namespace name entered. by used and get the namesapce object from cluster 662 | for _, element := range src.GetNamespaces() { 663 | if element == "kube-system" || element == "kube-public" || element == "kube-node-lease" { 664 | continue 665 | } 666 | ns, err := src.GetClientset().CoreV1().Namespaces().Get(context.TODO(), element, metav1.GetOptions{}) 667 | if err != nil { 668 | fmt.Printf("Could not List kubernetes namespaces using cluster client: %v\n", err) 669 | os.Exit(1) 670 | } 671 | 672 | resource.Nsl.Items = append(resource.Nsl.Items, *ns) 673 | } 674 | } else { 675 | fmt.Println("Namespace list entered as 'all' by user, hence all namespaces will be considered") 676 | resource.Nsl, err = src.GetClientset().CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) 677 | if err != nil { 678 | fmt.Printf("Could not List kubernetes namespaces using cluster client: %v\n", err) 679 | os.Exit(1) 680 | } 681 | 682 | j := 0 683 | for _, element := range resource.Nsl.Items { 684 | if element.ObjectMeta.Name != "kube-system" && element.ObjectMeta.Name != "kube-public" && element.ObjectMeta.Name != "kube-node-lease" { 685 | resource.Nsl.Items[j] = element 686 | j++ 687 | } 688 | } 689 | resource.Nsl.Items = resource.Nsl.Items[:j] 690 | } 691 | 692 | } 693 | 694 | //Scan source kubernetes cluster and generate the Helm charts 695 | func Generate_helm_charts(src *cluster.Cluster, resource *resource.Resources) { 696 | labelSelector := fmt.Sprintf("owner=helm") 697 | listOptions := metav1.ListOptions{ 698 | LabelSelector: labelSelector, 699 | } 700 | 701 | resource.HelmList = make(map[string]map[string]string) 702 | for _, element := range resource.Nsl.Items { 703 | var helmCharts = make(map[string]helm.Release) 704 | 705 | secretList, err := src.GetClientset().CoreV1().Secrets(element.ObjectMeta.Name).List(context.TODO(), listOptions) 706 | if err != nil { 707 | fmt.Printf("Could not read kubernetes Secrets using cluster client: %v\n", err) 708 | os.Exit(1) 709 | } 710 | 711 | for _, secret := range secretList.Items { 712 | status_tmp := secret.ObjectMeta.Labels["status"] 713 | status := strings.Split(status_tmp, ":") 714 | if status[0] == "deployed" { 715 | 716 | base64Text := make([]byte, base64.StdEncoding.DecodedLen(len(secret.Data["release"]))) 717 | base64.StdEncoding.Decode(base64Text, []byte(secret.Data["release"])) 718 | 719 | var secret_uncompressed bytes.Buffer 720 | 721 | err = gunzipWrite(&secret_uncompressed, &base64Text) 722 | if err != nil { 723 | fmt.Println("test9") 724 | log.Fatal(err) 725 | } 726 | secret_string := secret_uncompressed.String() 727 | 728 | //convert the release string into helm release struct 729 | var secret_data helm.Release //map[string]interface{} 730 | json.Unmarshal([]byte(secret_string), &secret_data) 731 | 732 | //Add the chart name to the helmCharts global variable 733 | helmCharts[secret_data.Name] = secret_data 734 | path := src.Helm_path + "/KMFHelmCharts/namespaces/" + element.ObjectMeta.Name 735 | writeChartToFile(helmCharts, path, element.ObjectMeta.Name, resource) 736 | } 737 | 738 | } 739 | } 740 | } 741 | 742 | func writeChartToFile(charts map[string]helm.Release, path string, namespace string, resource *resource.Resources) { 743 | // Create the directory locally to store the helm charts 744 | fmt.Println("Path :", path) 745 | if _, err := os.Stat(path); os.IsNotExist(err) { 746 | err = os.Mkdir(path, 0700) 747 | if err != nil { 748 | fmt.Println("Error creating the path for helm charts\n", err) 749 | } 750 | } 751 | 752 | var chartsPath = make(map[string]string) 753 | //var helmCharts = make(map[string]helm.Release) 754 | // Get the ctarts from release struct 755 | for k, v := range charts { 756 | 757 | // Create subdirectory to store the charts for this release 758 | if _, err := os.Stat(path + "/" + v.Name); os.IsNotExist(err) { 759 | err = os.Mkdir(path+"/"+v.Name, 0700) 760 | if err != nil { 761 | fmt.Println("Error creating the path for helm release: \n", err, v.Name) 762 | } 763 | } 764 | 765 | // Add path to the chart to the HelmList to later install the chart from this path on EKS cluster 766 | chartsPath[v.Name] = path + "/" + v.Name 767 | 768 | helm_templates := v.Chart.Templates 769 | fmt.Println("Chart Name:", k) 770 | //fmt.Println("template:", secret.Data["release"]) 771 | for _, element := range helm_templates { 772 | 773 | fmt.Println("secrets:", element.Name) 774 | 775 | //fmt.Println("template:", string(element.Data)) 776 | if _, err := os.Stat(filepath.Dir(path + "/" + v.Name + "/" + element.Name)); os.IsNotExist(err) { 777 | os.MkdirAll(filepath.Dir(path+"/"+v.Name+"/"+element.Name), 0700) 778 | } 779 | err := ioutil.WriteFile(path+"/"+v.Name+"/"+element.Name, element.Data, 0600) 780 | if err != nil { 781 | panic(err) 782 | } 783 | } 784 | 785 | helm_files := v.Chart.Files 786 | for _, element := range helm_files { 787 | fmt.Println("Files Name:", element.Name) 788 | //fmt.Println("template:", string(element.Data)) 789 | if _, err := os.Stat(filepath.Dir(path + "/" + v.Name + "/" + element.Name)); os.IsNotExist(err) { 790 | os.MkdirAll(filepath.Dir(path+"/"+v.Name+"/"+element.Name), 0700) 791 | } 792 | err := ioutil.WriteFile(path+"/"+v.Name+"/"+element.Name, element.Data, 0600) 793 | if err != nil { 794 | panic(err) 795 | } 796 | } 797 | 798 | //Write values file 799 | if _, err := os.Stat(filepath.Dir(path + "/" + v.Name + "/" + "values.json")); os.IsNotExist(err) { 800 | os.MkdirAll(filepath.Dir(path+"/"+v.Name+"/"+"values.json"), 0700) 801 | } 802 | 803 | jsonString, err := json.Marshal(v.Chart.Values) 804 | valuesyaml, err := yaml.JSONToYAML(jsonString) 805 | err = ioutil.WriteFile(path+"/"+v.Name+"/"+"values.yaml", valuesyaml, 0600) 806 | if err != nil { 807 | panic(err) 808 | } 809 | 810 | //Write Chart metadata to chart.json file 811 | if _, err := os.Stat(filepath.Dir(path + "/" + v.Name + "/" + "Chart.yaml")); os.IsNotExist(err) { 812 | os.MkdirAll(filepath.Dir(path+"/"+v.Name+"/"+"Chart.yaml"), 0700) 813 | } 814 | 815 | jsonString, err = json.Marshal(v.Chart.Metadata) 816 | chartyaml, err := yaml.JSONToYAML(jsonString) 817 | err = ioutil.WriteFile(path+"/"+v.Name+"/"+"Chart.yaml", chartyaml, 0600) 818 | if err != nil { 819 | panic(err) 820 | } 821 | } 822 | 823 | resource.HelmList[namespace] = chartsPath 824 | } 825 | 826 | func gunzipWrite(w io.Writer, data *[]byte) error { 827 | // Write gzipped data to the client 828 | 829 | gr, err := gzip.NewReader(bytes.NewBuffer(*data)) 830 | if err != nil { 831 | log.Fatal(err) 832 | return err 833 | } 834 | defer gr.Close() 835 | *data, err = ioutil.ReadAll(gr) 836 | 837 | w.Write(*data) 838 | return nil 839 | } 840 | -------------------------------------------------------------------------------- /app/target/eks/eks.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: MIT-0 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | * software and associated documentation files (the "Software"), to deal in the Software 7 | * without restriction, including without limitation the rights to use, copy, modify, 8 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | 20 | package aks 21 | import ( 22 | "fmt" 23 | cluster "containers-migration-factory/app/cluster" 24 | resource "containers-migration-factory/app/resource" 25 | target_impl "containers-migration-factory/app/target/target_impl" 26 | ) 27 | 28 | // AKS defines as source 29 | type EKS struct{} 30 | 31 | func (c EKS) Connect(sCluster *cluster.Cluster) { 32 | sCluster.Generate_cluster_client() 33 | } 34 | 35 | func (c EKS) GetTargetDetails(sCluster *cluster.Cluster) resource.Resources { 36 | fmt.Println("EKS GetTargetDetails....") 37 | resources := resource.Resources{} 38 | 39 | return resources 40 | } 41 | 42 | func (c EKS) DeployResources(sCluster *cluster.Cluster, srcResources *resource.Resources,action string) { 43 | fmt.Println("EKS Deploying resources....") 44 | 45 | if action == "Deploy" { 46 | target_impl.Deploy_resource_eks(sCluster, srcResources) 47 | } 48 | 49 | // Functions to delete resource from Destination EKS cluster 50 | if action == "Delete" { 51 | target_impl.Delete_helm_charts(sCluster, srcResources) 52 | target_impl.Delete_resource_eks(sCluster, srcResources) 53 | } 54 | 55 | } 56 | 57 | 58 | // GCP FormatSourceData implements the Geometry interface 59 | func (c EKS) FormatSourceData(resource *resource.Resources) { 60 | } 61 | -------------------------------------------------------------------------------- /app/target/target.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: MIT-0 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | * software and associated documentation files (the "Software"), to deal in the Software 7 | * without restriction, including without limitation the rights to use, copy, modify, 8 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | 20 | package source 21 | 22 | import ( 23 | cluster "containers-migration-factory/app/cluster" 24 | resource "containers-migration-factory/app/resource" 25 | ) 26 | 27 | // Geometry is an interface that defines Geometrical Calculation 28 | type Target interface { 29 | Connect(sCluster *cluster.Cluster) 30 | // GetSourceDetails(sCluster *cluster.Cluster) resource.Resources 31 | DeployResources(sCluster *cluster.Cluster,srcResources *resource.Resources,action string) 32 | FormatSourceData (resource *resource.Resources) // trim / data clean up 33 | } 34 | 35 | func SetContext(target Target,dCluster *cluster.Cluster){ 36 | /*Connect to target cluster*/ 37 | target.Connect(dCluster) 38 | } 39 | 40 | // Invoke specific source based on input provided 41 | func Invoke(target Target, sType string, sCluster *cluster.Cluster, dCluster *cluster.Cluster,srcResources *resource.Resources, action string) string { 42 | 43 | /*Connect to target cluster*/ 44 | // target.Connect(dCluster) 45 | 46 | target.DeployResources(dCluster,srcResources,action) 47 | 48 | /*Get Source Details*/ 49 | 50 | // resources := source.GetSourceDetails(sCluster) 51 | 52 | // source.FormatSourceData(&resources) 53 | 54 | return "success" 55 | } 56 | -------------------------------------------------------------------------------- /app/target/target_impl/target_impl.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: MIT-0 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | * software and associated documentation files (the "Software"), to deal in the Software 7 | * without restriction, including without limitation the rights to use, copy, modify, 8 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | 20 | package AWS 21 | 22 | import ( 23 | "context" 24 | "fmt" 25 | "github.com/gofrs/flock" 26 | "github.com/pkg/errors" 27 | "gopkg.in/yaml.v2" 28 | "helm.sh/helm/v3/pkg/action" 29 | "helm.sh/helm/v3/pkg/chart" 30 | "helm.sh/helm/v3/pkg/cli" 31 | "helm.sh/helm/v3/pkg/cli/values" 32 | "helm.sh/helm/v3/pkg/downloader" 33 | "helm.sh/helm/v3/pkg/getter" 34 | "helm.sh/helm/v3/pkg/repo" 35 | "io/ioutil" 36 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 37 | "log" 38 | "os" 39 | "os/exec" 40 | "path/filepath" 41 | "strings" 42 | "sync" 43 | "time" 44 | "helm.sh/helm/v3/pkg/chart/loader" 45 | "helm.sh/helm/v3/pkg/strvals" 46 | cluster "containers-migration-factory/app/cluster" 47 | resource "containers-migration-factory/app/resource" 48 | ) 49 | 50 | 51 | type Config struct { 52 | CurrentContext string `yaml:"current-context"` 53 | } 54 | 55 | var config Config 56 | var settings *cli.EnvSettings 57 | 58 | func stringInSlice(a string, list []string) bool { 59 | for _, b := range list { 60 | if strings.ToLower(b) == a { 61 | return true 62 | } 63 | } 64 | return false 65 | } 66 | 67 | func Deploy_resource_eks(dst *cluster.Cluster, src_resources *resource.Resources) { 68 | 69 | // Create non-namespaces resources 70 | if stringInSlice("mutatingWebhookconfigurations", dst.Resources) || stringInSlice("mutatingwebhookconfiguration", dst.Resources) || stringInSlice("all", dst.Resources) { 71 | // Create list of MutatingWebhookCOnfiguration in destination cluster 72 | for _, element := range src_resources.MutatingWebhookConfigurationList { 73 | element := element 74 | fmt.Println("Creating MutatingWebhook: ", element.ObjectMeta.Name) 75 | _, err := dst.Clientset.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(context.TODO(), &element, metav1.CreateOptions{}) 76 | if err != nil { 77 | fmt.Println(err) 78 | } 79 | 80 | } 81 | } 82 | 83 | // Create list of ValidatingWebhookCOnfiguration in destination cluster 84 | if stringInSlice("validatingwebhookconfiguration", dst.Resources) || stringInSlice("validatingwebhookconfigurations", dst.Resources) || stringInSlice("all", dst.Resources) { 85 | for _, element := range src_resources.ValidatingWebhookConfigurationList { 86 | element := element 87 | fmt.Println("Creating MutatingWebhook: ", element.ObjectMeta.Name) 88 | _, err := dst.Clientset.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(context.TODO(), &element, metav1.CreateOptions{}) 89 | if err != nil { 90 | fmt.Println(err) 91 | } 92 | } 93 | } 94 | 95 | // Create list of namespaces in destination cluster 96 | for _, element := range src_resources.Nsl.Items { 97 | element := element 98 | fmt.Println("Creating the namespace: ", element.ObjectMeta.Name) 99 | _, err := dst.Clientset.CoreV1().Namespaces().Create(context.TODO(), &element, metav1.CreateOptions{}) 100 | if err != nil { 101 | fmt.Println(err) 102 | } 103 | } 104 | 105 | // Install/Upgrade helm charts 106 | Deploy_helm_charts(dst, src_resources) 107 | 108 | // Loop through each namespace and create resources inside each namespace 109 | for _, element := range src_resources.Nsl.Items { 110 | element := element 111 | 112 | fmt.Println("=====================================================================") 113 | fmt.Println("Operating on namespace: ", element.ObjectMeta.Name) 114 | fmt.Println("=====================================================================") 115 | 116 | // Create secrets resource 117 | if stringInSlice("secrets", dst.Resources) || stringInSlice("secret", dst.Resources) || stringInSlice("all", dst.Resources) { 118 | fmt.Println("===============") 119 | fmt.Println("Creating Secrets") 120 | for _, secret := range src_resources.SecretList { 121 | secret := secret 122 | if secret.ObjectMeta.Namespace == element.ObjectMeta.Name { 123 | fmt.Println("Creating Secret: ", secret.ObjectMeta.Name) 124 | _, err := dst.Clientset.CoreV1().Secrets(element.ObjectMeta.Name).Create(context.TODO(), &secret, metav1.CreateOptions{}) 125 | if err != nil { 126 | fmt.Println(err) 127 | } 128 | } 129 | } 130 | } 131 | 132 | // Create ConfigMap resource 133 | if stringInSlice("configmaps", dst.Resources) || stringInSlice("configmap", dst.Resources) || stringInSlice("cm", dst.Resources) || stringInSlice("all", dst.Resources) { 134 | fmt.Println("===============") 135 | fmt.Println("Creating ConfigMap's") 136 | for _, cm := range src_resources.ConfigMapsList { 137 | cm := cm 138 | if cm.ObjectMeta.Namespace == element.ObjectMeta.Name { 139 | fmt.Println("Creating ConfigMap: ", cm.ObjectMeta.Name) 140 | _, err := dst.Clientset.CoreV1().ConfigMaps(element.ObjectMeta.Name).Create(context.TODO(), &cm, metav1.CreateOptions{}) 141 | if err != nil { 142 | fmt.Println(err) 143 | } 144 | } 145 | } 146 | } 147 | 148 | // Create StorageClass resource 149 | if stringInSlice("storageclasses", dst.Resources) || stringInSlice("storageclass", dst.Resources) || stringInSlice("sc", dst.Resources) || stringInSlice("all", dst.Resources) { 150 | fmt.Println("===============") 151 | fmt.Println("Creating StorageClasses") 152 | for _, sc := range src_resources.StorageClassList { 153 | sc := sc 154 | if sc.ObjectMeta.Namespace == element.ObjectMeta.Name { 155 | fmt.Println("Creating StorageClass: ", sc.ObjectMeta.Name) 156 | _, err := dst.Clientset.StorageV1().StorageClasses().Create(context.TODO(), &sc, metav1.CreateOptions{}) 157 | if err != nil { 158 | fmt.Println(err) 159 | } 160 | } 161 | } 162 | } 163 | 164 | // Create PVC resource 165 | if stringInSlice("persistentvolumeclaims", dst.Resources) || stringInSlice("persistentvolumeclaim", dst.Resources) || stringInSlice("pvc", dst.Resources) || stringInSlice("all", dst.Resources) { 166 | fmt.Println("===============") 167 | fmt.Println("Creating PersistentVolumeClaims") 168 | for _, pvc := range src_resources.PersistentVolumeClaimsList { 169 | pvc := pvc 170 | if pvc.ObjectMeta.Namespace == element.ObjectMeta.Name { 171 | fmt.Println("Creating PVC: ", pvc.ObjectMeta.Name) 172 | _, err := dst.Clientset.CoreV1().PersistentVolumeClaims(element.ObjectMeta.Name).Create(context.TODO(), &pvc, metav1.CreateOptions{}) 173 | if err != nil { 174 | fmt.Println(err) 175 | } 176 | } 177 | } 178 | } 179 | 180 | // Create deployment resource 181 | if stringInSlice("deployment", dst.Resources) || stringInSlice("deployments", dst.Resources) || stringInSlice("deploy", dst.Resources) || stringInSlice("all", dst.Resources) { 182 | fmt.Println("===============") 183 | fmt.Println("Creating Deployment") 184 | for _, dep := range src_resources.Depl { 185 | dep := dep 186 | if dep.ObjectMeta.Namespace == element.ObjectMeta.Name { 187 | fmt.Println("Creating Deployment: ", dep.ObjectMeta.Name) 188 | _, err := dst.Clientset.AppsV1().Deployments(element.ObjectMeta.Name).Create(context.TODO(), &dep, metav1.CreateOptions{}) 189 | if err != nil { 190 | fmt.Println(err) 191 | } 192 | } 193 | } 194 | } 195 | 196 | // Create Service resource 197 | if stringInSlice("service", dst.Resources) || stringInSlice("svc", dst.Resources) || stringInSlice("all", dst.Resources) { 198 | fmt.Println("===============") 199 | fmt.Println("Creating Service") 200 | for _, svc := range src_resources.Svcl { 201 | svc := svc 202 | if svc.ObjectMeta.Namespace == element.ObjectMeta.Name { 203 | svc.Spec.ClusterIP = "" 204 | for port, _ := range svc.Spec.Ports { 205 | svc.Spec.Ports[port].NodePort = 0 206 | } 207 | fmt.Println("Creating Service: ", svc.ObjectMeta.Name) 208 | _, err := dst.Clientset.CoreV1().Services(element.ObjectMeta.Name).Create(context.TODO(), &svc, metav1.CreateOptions{}) 209 | if err != nil { 210 | fmt.Println(err) 211 | } 212 | } 213 | } 214 | } 215 | 216 | // Create Daemonset resource 217 | if stringInSlice("daemonset", dst.Resources) || stringInSlice("daemonsets", dst.Resources) || stringInSlice("ds", dst.Resources) || stringInSlice("all", dst.Resources) { 218 | fmt.Println("===============") 219 | fmt.Println("Creating Daemonset") 220 | for _, ds := range src_resources.Dsl { 221 | ds := ds 222 | if ds.ObjectMeta.Namespace == element.ObjectMeta.Name { 223 | fmt.Println("Creating Daemonset: ", ds.ObjectMeta.Name) 224 | _, err := dst.Clientset.AppsV1().DaemonSets(element.ObjectMeta.Name).Create(context.TODO(), &ds, metav1.CreateOptions{}) 225 | if err != nil { 226 | fmt.Println(err) 227 | } 228 | } 229 | } 230 | } 231 | 232 | // Create Ingress resource 233 | if stringInSlice("ingresses", dst.Resources) || stringInSlice("ingress", dst.Resources) || stringInSlice("ing", dst.Resources) || stringInSlice("all", dst.Resources) { 234 | fmt.Println("===============") 235 | fmt.Println("Creating Ingresses") 236 | for _, ingress := range src_resources.IngressList { 237 | ingress := ingress 238 | if ingress.ObjectMeta.Namespace == element.ObjectMeta.Name { 239 | fmt.Println("Creating Ingress: ", ingress.ObjectMeta.Name) 240 | _, err := dst.Clientset.NetworkingV1().Ingresses(element.ObjectMeta.Name).Create(context.TODO(), &ingress, metav1.CreateOptions{}) 241 | if err != nil { 242 | fmt.Println(err) 243 | } 244 | } 245 | } 246 | } 247 | 248 | // Create Roles resource 249 | if stringInSlice("role", dst.Resources) || stringInSlice("roles", dst.Resources) || stringInSlice("all", dst.Resources) { 250 | fmt.Println("===============") 251 | fmt.Println("Creating Roles") 252 | for _, rl := range src_resources.RoleList { 253 | rl := rl 254 | if rl.ObjectMeta.Namespace == element.ObjectMeta.Name { 255 | fmt.Println("Creating Roles: ", rl.ObjectMeta.Name) 256 | _, err := dst.Clientset.RbacV1().Roles(element.ObjectMeta.Name).Create(context.TODO(), &rl, metav1.CreateOptions{}) 257 | if err != nil { 258 | fmt.Println(err) 259 | } 260 | } 261 | } 262 | } 263 | 264 | // Create Role Bindings resource 265 | if stringInSlice("rolebinding", dst.Resources) || stringInSlice("rolebindings", dst.Resources) || stringInSlice("all", dst.Resources) { 266 | fmt.Println("===============") 267 | fmt.Println("Creating Role Bindings") 268 | for _, rbl := range src_resources.RoleBindingList { 269 | rbl := rbl 270 | if rbl.ObjectMeta.Namespace == element.ObjectMeta.Name { 271 | fmt.Println("Creating Role Bindings: ", rbl.ObjectMeta.Name) 272 | _, err := dst.Clientset.RbacV1().RoleBindings(element.ObjectMeta.Name).Create(context.TODO(), &rbl, metav1.CreateOptions{}) 273 | if err != nil { 274 | fmt.Println(err) 275 | } 276 | } 277 | } 278 | } 279 | 280 | // Create secrets resource 281 | if stringInSlice("jobs", dst.Resources) || stringInSlice("job", dst.Resources) || stringInSlice("all", dst.Resources) { 282 | fmt.Println("===============") 283 | fmt.Println("Creating Secrets") 284 | for _, secret := range src_resources.SecretList { 285 | secret := secret 286 | if secret.ObjectMeta.Namespace == element.ObjectMeta.Name { 287 | fmt.Println("Creating Secret: ", secret.ObjectMeta.Name) 288 | _, err := dst.Clientset.CoreV1().Secrets(element.ObjectMeta.Name).Create(context.TODO(), &secret, metav1.CreateOptions{}) 289 | if err != nil { 290 | fmt.Println(err) 291 | } 292 | } 293 | } 294 | } 295 | 296 | // Create StorageClass resource 297 | if stringInSlice("storageclasses", dst.Resources) || stringInSlice("storageclass", dst.Resources) || stringInSlice("sc", dst.Resources) || stringInSlice("all", dst.Resources) { 298 | fmt.Println("===============") 299 | fmt.Println("Creating StorageClasses") 300 | for _, sc := range src_resources.StorageClassList { 301 | sc := sc 302 | if sc.ObjectMeta.Namespace == element.ObjectMeta.Name { 303 | fmt.Println("Creating StorageClass: ", sc.ObjectMeta.Name) 304 | _, err := dst.Clientset.StorageV1().StorageClasses().Create(context.TODO(), &sc, metav1.CreateOptions{}) 305 | if err != nil { 306 | fmt.Println(err) 307 | } 308 | } 309 | } 310 | } 311 | 312 | // Create CronJob resource 313 | if stringInSlice("cronjobs", dst.Resources) || stringInSlice("cronjob", dst.Resources) || stringInSlice("cj", dst.Resources) || stringInSlice("all", dst.Resources) { 314 | fmt.Println("===============") 315 | fmt.Println("Creating CronJob's") 316 | for _, cronjob := range src_resources.CronJobList { 317 | cronjob := cronjob 318 | if cronjob.ObjectMeta.Namespace == element.ObjectMeta.Name { 319 | fmt.Println("Creating CronJob: ", cronjob.ObjectMeta.Name) 320 | _, err := dst.Clientset.BatchV1beta1().CronJobs(element.ObjectMeta.Name).Create(context.TODO(), &cronjob, metav1.CreateOptions{}) 321 | if err != nil { 322 | fmt.Println(err) 323 | } 324 | } 325 | } 326 | } 327 | 328 | // Create job resource 329 | if stringInSlice("job", dst.Resources) || stringInSlice("jobs", dst.Resources) || stringInSlice("all", dst.Resources) { 330 | fmt.Println("===============") 331 | fmt.Println("Creating Job's") 332 | for _, job := range src_resources.JobList { 333 | job := job 334 | if job.ObjectMeta.Namespace == element.ObjectMeta.Name { 335 | fmt.Println("Creating Job's: ", job.ObjectMeta.Name) 336 | _, err := dst.Clientset.BatchV1().Jobs(element.ObjectMeta.Name).Create(context.TODO(), &job, metav1.CreateOptions{}) 337 | if err != nil { 338 | fmt.Println(err) 339 | } 340 | } 341 | } 342 | } 343 | 344 | // Create Cluster Cluster Role resource 345 | if stringInSlice("clusterrole", dst.Resources) || stringInSlice("clusterroles", dst.Resources) || stringInSlice("all", dst.Resources) { 346 | fmt.Println("===============") 347 | fmt.Println("Creating Cluster Roles") 348 | for _, crl := range src_resources.ClusterRoleList { 349 | crl := crl 350 | if crl.ObjectMeta.Namespace == element.ObjectMeta.Name { 351 | fmt.Println("Creating Role Bindings: ", crl.ObjectMeta.Name) 352 | _, err := dst.Clientset.RbacV1().ClusterRoles().Create(context.TODO(), &crl, metav1.CreateOptions{}) 353 | if err != nil { 354 | fmt.Println(err) 355 | } 356 | } 357 | } 358 | } 359 | 360 | // Create Cluster Role Binding resource 361 | if stringInSlice("clusterrolebinding", dst.Resources) || stringInSlice("clusterrolebindings", dst.Resources) || stringInSlice("all", dst.Resources) { 362 | fmt.Println("===============") 363 | fmt.Println("Creating Cluster Role Bindings") 364 | for _, crbl := range src_resources.ClusterRoleBindingList { 365 | crbl := crbl 366 | if crbl.ObjectMeta.Namespace == element.ObjectMeta.Name { 367 | fmt.Println("Creating Role Bindings: ", crbl.ObjectMeta.Name) 368 | _, err := dst.Clientset.RbacV1().ClusterRoleBindings().Create(context.TODO(), &crbl, metav1.CreateOptions{}) 369 | if err != nil { 370 | fmt.Println(err) 371 | } 372 | } 373 | } 374 | } 375 | 376 | // Create Hpa resource 377 | if stringInSlice("horizontalpodautoscaler", dst.Resources) || stringInSlice("horizontalpodautoscalers", dst.Resources) || stringInSlice("hpa", dst.Resources) || stringInSlice("all", dst.Resources) { 378 | fmt.Println("===============") 379 | fmt.Println("Creating HorizontalPodAutoscalers") 380 | for _, hpa := range src_resources.HpaList { 381 | hpa := hpa 382 | if hpa.ObjectMeta.Namespace == element.ObjectMeta.Name { 383 | fmt.Println("Creating HorizontalPodAutoscaler: ", hpa.ObjectMeta.Name) 384 | _, err := dst.Clientset.AutoscalingV1().HorizontalPodAutoscalers(element.ObjectMeta.Name).Create(context.TODO(), &hpa, metav1.CreateOptions{}) 385 | if err != nil { 386 | fmt.Println(err) 387 | } 388 | } 389 | } 390 | } 391 | 392 | // Create Pod Security Policy resource 393 | if stringInSlice("podsecuritypolicies", dst.Resources) || stringInSlice("podsecuritypolicy", dst.Resources) || stringInSlice("psp", dst.Resources) || stringInSlice("all", dst.Resources) { 394 | fmt.Println("===============") 395 | fmt.Println("Creating Pod security policies") 396 | for _, psp := range src_resources.PspList { 397 | psp := psp 398 | if psp.ObjectMeta.Namespace == element.ObjectMeta.Name { 399 | fmt.Println("Creating Pod Security Policies: ", psp.ObjectMeta.Name) 400 | _, err := dst.Clientset.PolicyV1beta1().PodSecurityPolicies().Create(context.TODO(), &psp, metav1.CreateOptions{}) 401 | if err != nil { 402 | fmt.Println(err) 403 | } 404 | } 405 | } 406 | } 407 | 408 | // Create Service Account resource 409 | if stringInSlice("serviceaccount", dst.Resources) || stringInSlice("serviceaccounts", dst.Resources) || stringInSlice("sa", dst.Resources) || stringInSlice("all", dst.Resources) { 410 | fmt.Println("===============") 411 | fmt.Println("Creating Service Account Job's") 412 | for _, sa := range src_resources.SvcAccList { 413 | sa := sa 414 | if sa.ObjectMeta.Namespace == element.ObjectMeta.Name { 415 | fmt.Println("Creating Service Account: ", sa.ObjectMeta.Name) 416 | _, err := dst.Clientset.CoreV1().ServiceAccounts(element.ObjectMeta.Name).Create(context.TODO(), &sa, metav1.CreateOptions{}) 417 | if err != nil { 418 | fmt.Println(err) 419 | } 420 | } 421 | } 422 | } 423 | } 424 | } 425 | 426 | func Delete_resource_eks(dst *cluster.Cluster, src_resources *resource.Resources) { 427 | 428 | // Loop through each namespace and create resources inside each namespace 429 | for _, element := range src_resources.Nsl.Items { 430 | element := element 431 | fmt.Println("=====================================================================") 432 | fmt.Println("Operating on namespace: ", element.ObjectMeta.Name) 433 | fmt.Println("=====================================================================") 434 | // Delete PVC resource 435 | for _, pvc := range src_resources.PersistentVolumeClaimsList { 436 | if pvc.ObjectMeta.Namespace == element.ObjectMeta.Name { 437 | fmt.Println("Deleting PVC: ", pvc.ObjectMeta.Name) 438 | err := dst.Clientset.CoreV1().PersistentVolumeClaims(element.ObjectMeta.Name).Delete(context.TODO(), pvc.ObjectMeta.Name, metav1.DeleteOptions{}) 439 | if err != nil { 440 | fmt.Println(err) 441 | } 442 | } 443 | } 444 | 445 | // Delete Deployment resource 446 | fmt.Println("Deleting Deployments") 447 | for _, dep := range src_resources.Depl { 448 | dep := dep 449 | if dep.ObjectMeta.Namespace == element.ObjectMeta.Name { 450 | fmt.Println("Deleting Secret: ", dep.ObjectMeta.Name) 451 | err := dst.Clientset.AppsV1().Deployments(element.ObjectMeta.Name).Delete(context.TODO(), dep.ObjectMeta.Name, metav1.DeleteOptions{}) 452 | if err != nil { 453 | fmt.Println(err) 454 | } 455 | } 456 | } 457 | 458 | // Delete Service resource 459 | fmt.Println("Deleting Services") 460 | for _, svc := range src_resources.Svcl { 461 | svc := svc 462 | if svc.ObjectMeta.Namespace == element.ObjectMeta.Name { 463 | fmt.Println("Deleting Service: ", svc.ObjectMeta.Name) 464 | err := dst.Clientset.CoreV1().Services(element.ObjectMeta.Name).Delete(context.TODO(), svc.ObjectMeta.Name, metav1.DeleteOptions{}) 465 | if err != nil { 466 | fmt.Println(err) 467 | } 468 | } 469 | } 470 | 471 | // Delete DaemonSets resource 472 | fmt.Println("Deleting DaemonSets") 473 | for _, ds := range src_resources.Dsl { 474 | ds := ds 475 | if ds.ObjectMeta.Namespace == element.ObjectMeta.Name { 476 | fmt.Println("Deleting DaemonSet: ", ds.ObjectMeta.Name) 477 | err := dst.Clientset.AppsV1().DaemonSets(element.ObjectMeta.Name).Delete(context.TODO(), ds.ObjectMeta.Name, metav1.DeleteOptions{}) 478 | if err != nil { 479 | fmt.Println(err) 480 | } 481 | } 482 | } 483 | 484 | // Delete secrets resource 485 | fmt.Println("===============") 486 | fmt.Println("Deleting Secrets") 487 | for _, secret := range src_resources.SecretList { 488 | secret := secret 489 | if secret.ObjectMeta.Namespace == element.ObjectMeta.Name { 490 | fmt.Println("Deleting Secret: ", secret.ObjectMeta.Name) 491 | err := dst.Clientset.CoreV1().Secrets(element.ObjectMeta.Name).Delete(context.TODO(), secret.ObjectMeta.Name, metav1.DeleteOptions{}) 492 | if err != nil { 493 | fmt.Println(err) 494 | } 495 | } 496 | } 497 | 498 | // Delete StorageClass resource 499 | fmt.Println("===============") 500 | fmt.Println("Deleting StorageClass") 501 | for _, sc := range src_resources.StorageClassList { 502 | sc := sc 503 | if sc.ObjectMeta.Namespace == element.ObjectMeta.Name { 504 | fmt.Println("Deleting StorageClass: ", sc.ObjectMeta.Name) 505 | err := dst.Clientset.StorageV1().StorageClasses().Delete(context.TODO(), sc.ObjectMeta.Name, metav1.DeleteOptions{}) 506 | if err != nil { 507 | fmt.Println(err) 508 | } 509 | } 510 | } 511 | 512 | //Create MutatingWebhookConfiguration resource 513 | fmt.Println("===============") 514 | fmt.Println("Deleting MutatingWebhookConfiguration") 515 | for _, mwc := range src_resources.MutatingWebhookConfigurationList { 516 | mwc := mwc 517 | if mwc.ObjectMeta.Namespace == element.ObjectMeta.Name { 518 | fmt.Println("Deleting MutatingWebhookConfiguration: ", mwc.ObjectMeta.Name) 519 | err := dst.Clientset.AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(context.TODO(), mwc.ObjectMeta.Name, metav1.DeleteOptions{}) 520 | if err != nil { 521 | fmt.Println(err) 522 | } 523 | } 524 | } 525 | 526 | // Delete Configmap resource 527 | fmt.Println("===============") 528 | fmt.Println("Deleting ConfigMap's") 529 | for _, cm := range src_resources.ConfigMapsList { 530 | cm := cm 531 | if cm.ObjectMeta.Namespace == element.ObjectMeta.Name { 532 | fmt.Println("Deleting ConfigMap: ", cm.ObjectMeta.Name) 533 | err := dst.Clientset.CoreV1().ConfigMaps(element.ObjectMeta.Name).Delete(context.TODO(), cm.ObjectMeta.Name, metav1.DeleteOptions{}) 534 | if err != nil { 535 | fmt.Println(err) 536 | } 537 | } 538 | } 539 | 540 | // Delete CronJob resource 541 | fmt.Println("===============") 542 | fmt.Println("Deleting CronJob's") 543 | for _, cronjob := range src_resources.CronJobList { 544 | cronjob := cronjob 545 | if cronjob.ObjectMeta.Namespace == element.ObjectMeta.Name { 546 | fmt.Println("Deleting CronJob: ", cronjob.ObjectMeta.Name) 547 | err := dst.Clientset.BatchV1beta1().CronJobs(element.ObjectMeta.Name).Delete(context.TODO(), cronjob.ObjectMeta.Name, metav1.DeleteOptions{}) 548 | if err != nil { 549 | fmt.Println(err) 550 | } 551 | } 552 | } 553 | 554 | // Delete Job resource 555 | fmt.Println("===============") 556 | fmt.Println("Deleting Job's") 557 | for _, job := range src_resources.JobList { 558 | job := job 559 | if job.ObjectMeta.Namespace == element.ObjectMeta.Name { 560 | fmt.Println("Deleting Job: ", job.ObjectMeta.Name) 561 | err := dst.Clientset.BatchV1().Jobs(element.ObjectMeta.Name).Delete(context.TODO(), job.ObjectMeta.Name, metav1.DeleteOptions{}) 562 | if err != nil { 563 | fmt.Println(err) 564 | } 565 | } 566 | } 567 | 568 | // Delete ingress resource 569 | fmt.Println("===============") 570 | fmt.Println("Deleting Ingresses") 571 | for _, ingress := range src_resources.IngressList { 572 | ingress := ingress 573 | if ingress.ObjectMeta.Namespace == element.ObjectMeta.Name { 574 | fmt.Println("Deleting Ingress: ", ingress.ObjectMeta.Name) 575 | err := dst.Clientset.NetworkingV1().Ingresses(element.ObjectMeta.Name).Delete(context.TODO(), ingress.ObjectMeta.Name, metav1.DeleteOptions{}) 576 | if err != nil { 577 | fmt.Println(err) 578 | } 579 | } 580 | } 581 | 582 | // Delete HPA resource 583 | fmt.Println("===============") 584 | fmt.Println("Deleting HorizontalPodAutoscalers") 585 | for _, hpa := range src_resources.HpaList { 586 | hpa := hpa 587 | if hpa.ObjectMeta.Namespace == element.ObjectMeta.Name { 588 | fmt.Println("Deleting HorizontalPodAutoscaler: ", hpa.ObjectMeta.Name) 589 | err := dst.Clientset.AutoscalingV1().HorizontalPodAutoscalers(element.ObjectMeta.Name).Delete(context.TODO(), hpa.ObjectMeta.Name, metav1.DeleteOptions{}) 590 | if err != nil { 591 | fmt.Println(err) 592 | } 593 | } 594 | } 595 | 596 | // Delete psp resources 597 | fmt.Println("===============") 598 | fmt.Println("Deleting Pod Security Policies") 599 | for _, psp := range src_resources.PspList { 600 | psp := psp 601 | if psp.ObjectMeta.Namespace == element.ObjectMeta.Name { 602 | fmt.Println("Deleting PodSecurityPolicy: ", psp.ObjectMeta.Name) 603 | err := dst.Clientset.PolicyV1beta1().PodSecurityPolicies().Delete(context.TODO(), psp.ObjectMeta.Name, metav1.DeleteOptions{}) 604 | if err != nil { 605 | fmt.Println(err) 606 | } 607 | } 608 | } 609 | 610 | // Delete roles resource 611 | fmt.Println("===============") 612 | fmt.Println("Deleting Roles") 613 | for _, role := range src_resources.RoleList { 614 | role := role 615 | if role.ObjectMeta.Namespace == element.ObjectMeta.Name { 616 | fmt.Println("Deleting Roles: ", role.ObjectMeta.Name) 617 | err := dst.Clientset.RbacV1().Roles(element.ObjectMeta.Name).Delete(context.TODO(), role.ObjectMeta.Name, metav1.DeleteOptions{}) 618 | if err != nil { 619 | fmt.Println(err) 620 | } 621 | } 622 | } 623 | // Delete service account resources 624 | fmt.Println("===============") 625 | fmt.Println("Deleting Service Accounts") 626 | for _, sa := range src_resources.SvcAccList { 627 | sa := sa 628 | if sa.ObjectMeta.Namespace == element.ObjectMeta.Name { 629 | fmt.Println("Deleting Service Accounts: ", sa.ObjectMeta.Name) 630 | err := dst.Clientset.CoreV1().ServiceAccounts(element.ObjectMeta.Name).Delete(context.TODO(), sa.ObjectMeta.Name, metav1.DeleteOptions{}) 631 | if err != nil { 632 | fmt.Println(err) 633 | } 634 | } 635 | } 636 | 637 | } 638 | // Delete list of namespaces in destination cluster 639 | for _, element := range src_resources.Nsl.Items { 640 | 641 | element := element 642 | err := dst.Clientset.CoreV1().Namespaces().Delete(context.TODO(), element.ObjectMeta.Name, metav1.DeleteOptions{}) 643 | if err != nil { 644 | fmt.Println(err) 645 | } 646 | } 647 | } 648 | 649 | func Deploy_helm_charts(dst *cluster.Cluster, src_resources *resource.Resources) { 650 | args := map[string]string{} 651 | //repo := "" 652 | 653 | for namespace, charts := range src_resources.HelmList { 654 | 655 | for key, value := range charts { 656 | //name := key 657 | //chart := value 658 | fmt.Println("Installing Chart ", key, " on EKS cluster in namespace ", namespace) 659 | //InstallChart(key,"", value, args, namespace) 660 | namespace = namespace 661 | args = args 662 | // Resolve chart dependency 663 | cmd := exec.Command("helm", "dependency", "build") 664 | cmd.Dir = value 665 | out, err := cmd.Output() 666 | if err != nil { 667 | fmt.Println("test2") 668 | log.Fatal(err) 669 | } 670 | 671 | fmt.Printf(" %s\n", out) 672 | //install charts 673 | cmd = exec.Command("helm", "upgrade", "--install", key, "." , "-n", namespace) 674 | cmd.Dir = value 675 | out, err = cmd.Output() 676 | if err != nil { 677 | fmt.Println("Error installing Helm chart. If there is a helm chart already on target cluster with name ", key, " in failed state try deleting and run again") 678 | log.Fatal(err) 679 | } 680 | fmt.Printf(" %s\n", out) 681 | 682 | } 683 | } 684 | } 685 | 686 | func Delete_helm_charts(dst *cluster.Cluster, src_resources *resource.Resources) { 687 | args := map[string]string{} 688 | //repo := "" 689 | 690 | for namespace, charts := range src_resources.HelmList { 691 | 692 | for key, value := range charts { 693 | //name := key 694 | //chart := value 695 | fmt.Println("Uninstalling Chart ", key, " on EKS cluster") 696 | //InstallChart(key,"", value, args, namespace) 697 | namespace = namespace 698 | args = args 699 | //value = value 700 | 701 | //install charts 702 | cmd := exec.Command("helm", "uninstall", key, "-n", namespace) 703 | cmd.Dir = value 704 | out, err := cmd.Output() 705 | if err != nil { 706 | fmt.Println("Failed uninstalling chart ", key, " but continuing") 707 | } 708 | fmt.Printf(" %s\n", out) 709 | 710 | } 711 | } 712 | } 713 | 714 | // InstallChart 715 | func InstallChart(name, repo, chart string, args map[string]string, namespace string) { 716 | //args := map[string]string{} 717 | //repo := "" 718 | 719 | settings = cli.New() 720 | 721 | //ns := namespace 722 | 723 | actionConfig := new(action.Configuration) 724 | if err := actionConfig.Init(settings.RESTClientGetter(), namespace, os.Getenv("HELM_DRIVER"), debug); err != nil { 725 | log.Fatal(err) 726 | } 727 | client := action.NewInstall(actionConfig) 728 | 729 | if client.Version == "" && client.Devel { 730 | client.Version = ">0.0.0-0" 731 | } 732 | //name, chart, err := client.NameAndChart(args) 733 | client.ReleaseName = name 734 | cp, err := client.ChartPathOptions.LocateChart(fmt.Sprintf("%s/%s", repo, chart), settings) 735 | if err != nil { 736 | log.Fatal(err) 737 | } 738 | 739 | debug("CHART PATH: %s\n", cp) 740 | 741 | p := getter.All(settings) 742 | valueOpts := &values.Options{} 743 | vals, err := valueOpts.MergeValues(p) 744 | if err != nil { 745 | log.Fatal(err) 746 | } 747 | 748 | // Add args 749 | if err := strvals.ParseInto(args["set"], vals); err != nil { 750 | log.Fatal(errors.Wrap(err, "failed parsing --set data")) 751 | } 752 | 753 | // Check chart dependencies to make sure all are present in /charts 754 | chartRequested, err := loader.Load(cp) 755 | if err != nil { 756 | log.Fatal(err) 757 | } 758 | 759 | validInstallableChart, err := isChartInstallable(chartRequested) 760 | if !validInstallableChart { 761 | log.Fatal(err) 762 | } 763 | 764 | // IF charts have dependencies try to install it 765 | for _, dep := range chartRequested.Metadata.Dependencies { 766 | if dep.Enabled == true { 767 | fmt.Println("Chart Dependencies :", dep.Name) 768 | RepoAdd(dep.Name, dep.Repository) 769 | RepoUpdate() 770 | //InstallChart(dep.Name, dep.Repository,"",args,namespace) 771 | } 772 | } 773 | 774 | if req := chartRequested.Metadata.Dependencies; req != nil { 775 | // If CheckDependencies returns an error, we have unfulfilled dependencies. 776 | // As of Helm 2.4.0, this is treated as a stopping condition: 777 | // https://github.com/helm/helm/issues/2209 778 | fmt.Println("Checking Deps") 779 | if err := action.CheckDependencies(chartRequested, req); err != nil { 780 | fmt.Println("Downloading Deps") 781 | if client.DependencyUpdate { 782 | man := &downloader.Manager{ 783 | Out: os.Stdout, 784 | ChartPath: cp, 785 | Keyring: client.ChartPathOptions.Keyring, 786 | SkipUpdate: false, 787 | Getters: p, 788 | RepositoryConfig: settings.RepositoryConfig, 789 | RepositoryCache: settings.RepositoryCache, 790 | } 791 | if err := man.Update(); err != nil { 792 | log.Fatal(err) 793 | } 794 | } else { 795 | log.Fatal(err) 796 | } 797 | } 798 | } 799 | 800 | //fmt.Println("===============2============") 801 | 802 | client.Namespace = namespace 803 | release, err := client.Run(chartRequested, vals) 804 | if err != nil { 805 | log.Fatal(err) 806 | } 807 | fmt.Println(release.Manifest) 808 | } 809 | 810 | func isChartInstallable(ch *chart.Chart) (bool, error) { 811 | switch ch.Metadata.Type { 812 | case "", "application": 813 | return true, nil 814 | } 815 | return false, errors.Errorf("%s charts are not installable", ch.Metadata.Type) 816 | } 817 | 818 | func debug(format string, v ...interface{}) { 819 | format = fmt.Sprintf("[debug] %s\n", format) 820 | log.Output(2, fmt.Sprintf(format, v...)) 821 | } 822 | 823 | // RepoAdd adds repo with given name and url 824 | func RepoAdd(name, url string) { 825 | repoFile := settings.RepositoryConfig 826 | 827 | //Ensure the file directory exists as it is required for file locking 828 | err := os.MkdirAll(filepath.Dir(repoFile), os.ModePerm) 829 | if err != nil && !os.IsExist(err) { 830 | log.Fatal(err) 831 | } 832 | 833 | // Acquire a file lock for process synchronization 834 | fileLock := flock.New(strings.Replace(repoFile, filepath.Ext(repoFile), ".lock", 1)) 835 | lockCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 836 | defer cancel() 837 | locked, err := fileLock.TryLockContext(lockCtx, time.Second) 838 | if err == nil && locked { 839 | defer fileLock.Unlock() 840 | } 841 | if err != nil { 842 | log.Fatal(err) 843 | } 844 | 845 | b, err := ioutil.ReadFile(repoFile) 846 | if err != nil && !os.IsNotExist(err) { 847 | log.Fatal(err) 848 | } 849 | 850 | var f repo.File 851 | if err := yaml.Unmarshal(b, &f); err != nil { 852 | log.Fatal(err) 853 | } 854 | 855 | if f.Has(name) { 856 | fmt.Printf("repository name (%s) already exists\n", name) 857 | return 858 | } 859 | 860 | c := repo.Entry{ 861 | Name: name, 862 | URL: url, 863 | } 864 | 865 | r, err := repo.NewChartRepository(&c, getter.All(settings)) 866 | if err != nil { 867 | log.Fatal(err) 868 | } 869 | 870 | if _, err := r.DownloadIndexFile(); err != nil { 871 | err := errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", url) 872 | log.Fatal(err) 873 | } 874 | 875 | f.Update(&c) 876 | 877 | if err := f.WriteFile(repoFile, 0644); err != nil { 878 | log.Fatal(err) 879 | } 880 | fmt.Printf("%q has been added to your repositories\n", name) 881 | } 882 | 883 | // RepoUpdate updates charts for all helm repos 884 | func RepoUpdate() { 885 | repoFile := settings.RepositoryConfig 886 | 887 | f, err := repo.LoadFile(repoFile) 888 | if os.IsNotExist(errors.Cause(err)) || len(f.Repositories) == 0 { 889 | log.Fatal(errors.New("no repositories found. You must add one before updating")) 890 | } 891 | var repos []*repo.ChartRepository 892 | for _, cfg := range f.Repositories { 893 | r, err := repo.NewChartRepository(cfg, getter.All(settings)) 894 | if err != nil { 895 | log.Fatal(err) 896 | } 897 | repos = append(repos, r) 898 | } 899 | 900 | fmt.Printf("Hang tight while we grab the latest from your chart repositories...\n") 901 | var wg sync.WaitGroup 902 | for _, re := range repos { 903 | wg.Add(1) 904 | go func(re *repo.ChartRepository) { 905 | defer wg.Done() 906 | if _, err := re.DownloadIndexFile(); err != nil { 907 | fmt.Printf("...Unable to get an update from the %q chart repository (%s):\n\t%s\n", re.Config.Name, re.Config.URL, err) 908 | } else { 909 | fmt.Printf("...Successfully got an update from the %q chart repository\n", re.Config.Name) 910 | } 911 | }(re) 912 | } 913 | wg.Wait() 914 | fmt.Printf("Update Complete. ⎈ Happy Helming!⎈\n") 915 | } 916 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [COMMON] 2 | # common configuration params required for migration. 3 | # Local path where generated helm charts to be saved 4 | HELM_CHARTS_PATH=/Users/username/kuberenetes-pocs/helm 5 | RESOURCES=all 6 | # Valid Value for ACTION Deploy/Delete 7 | ACTION=Delete 8 | # Namespaces from which the resources need to migrated 9 | # comma seperated list of namespace or "all" 10 | NAMESPACES=all 11 | 12 | [SOURCE] 13 | # Source Cloud Provider valid values are GKE,AKE,KOPS 14 | CLOUD=GKE 15 | # Source kube config file 16 | KUBE_CONFIG=/Users/username/.kube/gcp.config 17 | CONTEXT=gke_cmf-aws_us-central1-c_cluster-1 18 | 19 | [TARGET] 20 | CLOUD=AWS 21 | # Target kube config file 22 | KUBE_CONFIG=/Users/username/.kube/config 23 | CONTEXT=arn:aws:eks:us-east-1:12233344444:cluster/eksworkshop-eksctl 24 | 25 | [MIGRATE_IMAGES] 26 | # Do you wish to migrate images from 3rd party repositories to Amazon Elastic Container Registry? Supply either "Yes" or "No" 27 | USERCONSENT=Yes 28 | # Comma separated list of 3rd party registries. Tool supports migration from gcr, gitlab, dockerhub registries. 29 | REGISTRY=GCR 30 | -------------------------------------------------------------------------------- /controllers/MIGRATE_IMAGES/MIGRATE_IMAGES_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: MIT-0 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | * software and associated documentation files (the "Software"), to deal in the Software 7 | * without restriction, including without limitation the rights to use, copy, modify, 8 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | package MIGRATE_IMAGES 20 | 21 | import ( 22 | "strings" 23 | "fmt" 24 | "regexp" 25 | "os/exec" 26 | "log" 27 | "github.com/aws/aws-sdk-go/aws" 28 | "github.com/aws/aws-sdk-go/aws/awserr" 29 | "github.com/aws/aws-sdk-go/aws/session" 30 | "github.com/aws/aws-sdk-go/service/ecr" 31 | "github.com/aws/aws-sdk-go/service/sts" 32 | ) 33 | 34 | type GetCallerIdentityOutput struct { 35 | // The AWS account ID number of the account that owns or contains the calling 36 | // entity. 37 | Account *string `type:"string"` 38 | 39 | // The AWS ARN associated with the calling entity. 40 | Arn *string `min:"20" type:"string"` 41 | 42 | // The unique identifier of the calling entity. The exact value depends on the 43 | // type of entity that is making the call. The values returned are those listed 44 | // in the aws:userid column in the Principal table (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_variables.html#principaltable) 45 | // found on the Policy Variables reference page in the IAM User Guide. 46 | UserId *string `type:"string"` 47 | } 48 | 49 | func create_ecr_repo(src_repo_name string, aws_region string) (ecr_create_status string){ 50 | svc := ecr.New(session.New(&aws.Config{Region: aws.String(aws_region)})) 51 | input := &ecr.CreateRepositoryInput{ 52 | RepositoryName: aws.String(src_repo_name), 53 | } 54 | 55 | result, err := svc.CreateRepository(input) 56 | if err != nil { 57 | if aerr, ok := err.(awserr.Error); ok { 58 | switch aerr.Code() { 59 | case ecr.ErrCodeServerException: 60 | fmt.Println(ecr.ErrCodeServerException, aerr.Error()) 61 | case ecr.ErrCodeInvalidParameterException: 62 | fmt.Println(ecr.ErrCodeInvalidParameterException, aerr.Error()) 63 | case ecr.ErrCodeInvalidTagParameterException: 64 | fmt.Println(ecr.ErrCodeInvalidTagParameterException, aerr.Error()) 65 | case ecr.ErrCodeTooManyTagsException: 66 | fmt.Println(ecr.ErrCodeTooManyTagsException, aerr.Error()) 67 | case ecr.ErrCodeRepositoryAlreadyExistsException: 68 | fmt.Println(ecr.ErrCodeRepositoryAlreadyExistsException, aerr.Error()) 69 | case ecr.ErrCodeLimitExceededException: 70 | fmt.Println(ecr.ErrCodeLimitExceededException, aerr.Error()) 71 | default: 72 | fmt.Println(aerr.Error()) 73 | } 74 | } else { 75 | // Print the error, cast err to awserr.Error to get the Code and 76 | // Message from an error. 77 | fmt.Println(err.Error()) 78 | } 79 | return 80 | } 81 | 82 | return *result.Repository.RepositoryUri 83 | } 84 | 85 | func list_ecr_repo(aws_region string) (ecr_repo_list []string) { 86 | svc := ecr.New(session.New(&aws.Config{Region: aws.String(aws_region)})) 87 | input := &ecr.DescribeRepositoriesInput{} 88 | 89 | result, err := svc.DescribeRepositories(input) 90 | if err != nil { 91 | if aerr, ok := err.(awserr.Error); ok { 92 | switch aerr.Code() { 93 | case ecr.ErrCodeServerException: 94 | fmt.Println(ecr.ErrCodeServerException, aerr.Error()) 95 | case ecr.ErrCodeInvalidParameterException: 96 | fmt.Println(ecr.ErrCodeInvalidParameterException, aerr.Error()) 97 | case ecr.ErrCodeRepositoryNotFoundException: 98 | fmt.Println(ecr.ErrCodeRepositoryNotFoundException, aerr.Error()) 99 | default: 100 | fmt.Println(aerr.Error()) 101 | } 102 | } else { 103 | // Print the error, cast err to awserr.Error to get the Code and 104 | // Message from an error. 105 | fmt.Println(err.Error()) 106 | } 107 | //return err 108 | } 109 | 110 | for _, repo_list := range result.Repositories { 111 | ecr_repo_list = append (ecr_repo_list, *repo_list.RepositoryUri) 112 | } 113 | return ecr_repo_list 114 | } 115 | 116 | func STS_GetCallerIdentity() (aws_account string) { 117 | svc := sts.New(session.New()) 118 | input := &sts.GetCallerIdentityInput{} 119 | 120 | result, err := svc.GetCallerIdentity(input) 121 | if err != nil { 122 | if aerr, ok := err.(awserr.Error); ok { 123 | switch aerr.Code() { 124 | default: 125 | fmt.Println(aerr.Error()) 126 | } 127 | } else { 128 | // Print the error, cast err to awserr.Error to get the Code and 129 | // Message from an error. 130 | fmt.Println(err.Error()) 131 | } 132 | return 133 | } 134 | aws_account = *result.Account 135 | return aws_account 136 | } 137 | 138 | func check_ecr_repo(src_image_name string, src_repo_name string, src_image_tag string) (updated_image_name string) { 139 | var validate_ecr string = "" 140 | var aws_region string 141 | var aws_account string 142 | //var stderr bytes.Buffer 143 | //var cmdout bytes.Buffer 144 | 145 | cmd := exec.Command("aws", "configure", "get", "region") 146 | out, err := cmd.Output() 147 | if err != nil { 148 | log.Fatal(err) 149 | } 150 | aws_region = strings.TrimSuffix(string(out), "\n") 151 | aws_account = STS_GetCallerIdentity() 152 | 153 | first_join := []string{fmt.Sprint(aws_account), "dkr", "ecr", string(aws_region), "amazonaws", "com"} 154 | ecr_reg_stage := strings.Join(first_join, ".") 155 | second_join := []string{ecr_reg_stage, src_repo_name} 156 | ecr_reg_url := strings.Join(second_join, "/") 157 | collect_ecr_reg_url := list_ecr_repo(aws_region) 158 | 159 | for _, list := range collect_ecr_reg_url { 160 | if list == ecr_reg_url { 161 | validate_ecr = "true" 162 | } 163 | } 164 | 165 | if validate_ecr != "true" { 166 | ecr_repo_name := create_ecr_repo(src_repo_name, aws_region) 167 | fmt.Println("Successfully created ECR repository named :", ecr_repo_name) 168 | } 169 | 170 | updated_image_stage := []string{ecr_reg_url, src_image_tag} 171 | updated_image_name = strings.Join(updated_image_stage, ":") 172 | 173 | srcimage_pull_cmd := exec.Command("docker", "pull", src_image_name) 174 | srcimagepullout, err := srcimage_pull_cmd.CombinedOutput() 175 | if err != nil { 176 | fmt.Println(fmt.Sprint(err) + ": " + string(srcimagepullout)) 177 | log.Fatal(err) 178 | } 179 | fmt.Printf("%s \n", srcimagepullout) 180 | 181 | image_tag_cmd := exec.Command("docker", "tag", src_image_name, updated_image_name) 182 | imagetagout, err := image_tag_cmd.CombinedOutput() 183 | if err != nil { 184 | fmt.Println(fmt.Sprint(err) + ": " + string(imagetagout)) 185 | log.Fatal(err) 186 | } 187 | fmt.Printf("%s \n", imagetagout) 188 | 189 | dstimage_push_cmd := exec.Command("docker", "push", updated_image_name) 190 | imagepushout, err := dstimage_push_cmd.CombinedOutput() 191 | if err != nil { 192 | fmt.Println(fmt.Sprint(err) + ": " + string(imagepushout)) 193 | log.Fatal(err) 194 | } 195 | fmt.Printf("%s \n", imagepushout) 196 | 197 | return updated_image_name 198 | 199 | } 200 | 201 | func Validate(src_image_name string, external_reg_names []string) (updated_image string) { 202 | updated_image = "" 203 | var src_image_tag string 204 | var src_registry_url string 205 | var src_registry_host string 206 | 207 | check_src_image_reg := strings.Split(src_image_name, ":") 208 | if len(check_src_image_reg) == 1 { 209 | src_image_tag = "latest" 210 | src_registry_url = check_src_image_reg[len(check_src_image_reg)-1] 211 | } else if len(check_src_image_reg) == 2 { 212 | src_image_tag = check_src_image_reg[len(check_src_image_reg)-1] 213 | src_registry_url = check_src_image_reg[len(check_src_image_reg)-2] 214 | } 215 | 216 | src_registry_url_split := strings.Split(src_registry_url, "/") 217 | 218 | if len(src_registry_url_split) != 1 { 219 | src_registry_host = src_registry_url_split[len(src_registry_url_split)-len(src_registry_url_split)] 220 | re := regexp.MustCompile(`\.`) 221 | regex_result := re.Match([]byte(src_registry_host)) 222 | if regex_result != true { 223 | src_registry_host = "dockerhub" 224 | } 225 | } 226 | 227 | for _, url := range external_reg_names { 228 | if src_registry_host == url { 229 | src_repo_name := strings.Join(src_registry_url_split[1:], "/") 230 | updated_image = check_ecr_repo(src_image_name, src_repo_name, src_image_tag) 231 | } 232 | } 233 | return updated_image 234 | } 235 | -------------------------------------------------------------------------------- /docs/kmf-architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-kubernetes-migration-factory/6d2cd34c5fc281e9049ce4fb305c3351d4bff84a/docs/kmf-architecture.jpg -------------------------------------------------------------------------------- /docs/kubernetes-migration-CLI-Logic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-kubernetes-migration-factory/6d2cd34c5fc281e9049ce4fb305c3351d4bff84a/docs/kubernetes-migration-CLI-Logic.jpg -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module containers-migration-factory 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/aws/aws-sdk-go v1.43.16 7 | github.com/bigkevmcd/go-configparser v0.0.0-20210106142102-909504547ead 8 | github.com/ghodss/yaml v1.0.0 9 | github.com/gofrs/flock v0.8.1 10 | github.com/pkg/errors v0.9.1 11 | gopkg.in/yaml.v2 v2.4.0 12 | gopkg.in/yaml.v3 v3.0.1 13 | helm.sh/helm/v3 v3.5.3 14 | k8s.io/api v0.20.5 15 | k8s.io/apimachinery v0.20.5 16 | k8s.io/client-go v0.20.5 17 | 18 | ) 19 | 20 | replace ( 21 | github.com/docker/distribution => github.com/docker/distribution v2.8.1+incompatible 22 | github.com/docker/docker => github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible 23 | github.com/opencontainers/image-spec => github.com/opencontainers/image-spec v1.0.2 24 | github.com/opencontainers/runc => github.com/opencontainers/runc v1.1.2 25 | ) 26 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: MIT-0 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | * software and associated documentation files (the "Software"), to deal in the Software 7 | * without restriction, including without limitation the rights to use, copy, modify, 8 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | 20 | package main 21 | 22 | import ( 23 | "bufio" 24 | //AWS "containers-migration-factory/controllers/AWS" 25 | //GCP "containers-migration-factory/controllers/GCP" 26 | "flag" 27 | "fmt" 28 | "io/ioutil" 29 | "path/filepath" 30 | "os" 31 | "strings" 32 | "unicode" 33 | 34 | "gopkg.in/yaml.v3" 35 | "github.com/bigkevmcd/go-configparser" 36 | 37 | gke "containers-migration-factory/app/source/gke" 38 | aks "containers-migration-factory/app/source/aks" 39 | kops "containers-migration-factory/app/source/kops" 40 | cluster "containers-migration-factory/app/cluster" 41 | source "containers-migration-factory/app/source" 42 | resource "containers-migration-factory/app/resource" 43 | eks "containers-migration-factory/app/target/eks" 44 | target "containers-migration-factory/app/target" 45 | ) 46 | 47 | type Config struct { 48 | CurrentContext string `yaml:"current-context"` 49 | } 50 | 51 | var config Config 52 | 53 | func fileExists(path string) bool { 54 | _, err := os.Stat(path) 55 | return !os.IsNotExist(err) 56 | } 57 | 58 | func Get_user_input(reader *bufio.Reader) (cluster.Cluster, cluster.Cluster, string, string) { 59 | sourceCluster := cluster.Cluster{} 60 | destCluster := cluster.Cluster{} 61 | 62 | 63 | namespaces_param := "" 64 | resources_param := "" 65 | helm_path_param := "" 66 | action_param := "" 67 | source_kubeconfig_param := "" 68 | source_context_param := "" 69 | src_cloud := "" 70 | destination_kubeconfig_param := "" 71 | destination_context_param := "" 72 | migrate_images_param := "" 73 | reg_names_param := "" 74 | 75 | if fileExists("config.ini"){ 76 | configParams, err := configparser.NewConfigParserFromFile("config.ini") 77 | if err != nil { 78 | fmt.Printf("Error opening the config file for parameters: %v\n", err) 79 | } else { 80 | // get common section 81 | common_options, err := configParams.Items("COMMON") 82 | if err == nil{ 83 | namespaces_param = common_options["NAMESPACES"] 84 | resources_param = common_options["RESOURCES"] 85 | helm_path_param = common_options["HELM_CHARTS_PATH"] 86 | action_param = common_options["ACTION"] 87 | } 88 | 89 | // get source section 90 | source_options, err := configParams.Items("SOURCE") 91 | if err == nil{ 92 | source_kubeconfig_param = source_options["KUBE_CONFIG"] 93 | source_context_param = source_options["CONTEXT"] 94 | src_cloud = source_options["CLOUD"] 95 | } 96 | 97 | // get target section 98 | target_options, err := configParams.Items("TARGET") 99 | if err == nil{ 100 | destination_kubeconfig_param = target_options["KUBE_CONFIG"] 101 | destination_context_param = target_options["CONTEXT"] 102 | // target_cloud := target_options["CLOUD"] 103 | } 104 | 105 | // get image migration section 106 | migrate_image_options, err := configParams.Items("MIGRATE_IMAGES") 107 | if err == nil{ 108 | migrate_images_param = migrate_image_options["USERCONSENT"] 109 | reg_names_param = migrate_image_options["REGISTRY"] 110 | } 111 | 112 | } 113 | } else{ 114 | fmt.Printf("Config.ini file doesn't exist and will use the user arguments\n") 115 | } 116 | //// Accept Source Cluster input 117 | source_kubeconfig := flag.String("source_kubeconfig", source_kubeconfig_param, "a string") 118 | namespaces := flag.String("namespaces", namespaces_param, "a string") 119 | destination_kubeconfig := flag.String("destination_kubeconfig", destination_kubeconfig_param, "a string") 120 | source_context := flag.String("source_context", source_context_param, "a string") 121 | destination_context := flag.String("destination_context", destination_context_param, "a string") 122 | resources := flag.String("resources", resources_param, "a string") 123 | helm_path := flag.String("helm_path", helm_path_param, "Path on local system where Helm charts from source cluster will be stored") 124 | migrate_images := flag.String("migrate_images", migrate_images_param, "User consent for migrating image from 3rd party registries to ECR") 125 | reg_names := flag.String("reg_names", reg_names_param, "List of 3rd party registries as comma separated items") 126 | action := flag.String("action", action_param, "What action the tools needs to perform. Accepted values are Deploy or Delete") 127 | sourceType := flag.String("source_type", src_cloud, "What is source type. Accepted values are GKE,AKS,KOPS") 128 | flag.Parse() 129 | 130 | // SOURCE =================== 131 | if *source_kubeconfig == "" { 132 | fmt.Print("Please pass the location of source kubernetes cluster kubeconfig file: ") 133 | *source_kubeconfig, _ = reader.ReadString('\n') 134 | //source_kubeconfig = "/home/ec2-user/.kube/gke" 135 | } 136 | 137 | // get current source context 138 | current_src_context := get_current_context(strings.TrimSuffix(*source_kubeconfig, "\n")) 139 | 140 | if *source_context == "" { 141 | fmt.Printf("Please pass the source context (default: %v): ", current_src_context) 142 | *source_context, _ = reader.ReadString('\n') 143 | } 144 | sourceCluster.SetContext( strings.TrimSuffix(*source_context, "\n") ) 145 | 146 | if *resources == "" { 147 | fmt.Printf("Please pass comma separated list of resources to migrate from source cluster to destination cluster. For all resources enter 'all': ") 148 | *resources, _ = reader.ReadString('\n') 149 | } 150 | 151 | if *namespaces == "" { 152 | fmt.Print("Please pass comma separated list of namespaces for source cluster. For all namespaces enter 'all': ") 153 | *namespaces, _ = reader.ReadString('\n') 154 | } 155 | 156 | if *helm_path == "" { 157 | fmt.Print("Please pass path to save Helm charts from source cluster: ") 158 | *helm_path, _ = reader.ReadString('\n') 159 | } 160 | 161 | sourceCluster.SetHelm_path ( strings.TrimSuffix(*helm_path, "\n") ) 162 | destCluster.SetHelm_path ( strings.TrimSuffix(*helm_path, "\n") ) 163 | 164 | // Remove the newline character from the end of filepath entered by user 165 | sourceCluster.SetKubeconfig_path ( strings.TrimSuffix(*source_kubeconfig, "\n") ) 166 | sourceCluster.SetContext ( strings.TrimSuffix(*source_context, "\n") ) 167 | 168 | // Migrate Images from source container registry to ECR 169 | if *migrate_images == "" { 170 | fmt.Print("Do you want to migrate images from 3rd party registries to ECR? Supply either Yes or No: ") 171 | *migrate_images, _ = reader.ReadString('\n') 172 | } 173 | 174 | // Remove the newline character from the end of filepath entered by user 175 | sourceCluster.SetMigrate_Images ( strings.TrimSuffix(*migrate_images, "\n")) 176 | 177 | if sourceCluster.GetMigrate_Images() == "Yes" || sourceCluster.GetMigrate_Images() == "yes" { 178 | if *reg_names == "" { 179 | fmt.Print("Tool supports migration from gcr, gitlab, dockerhub registries. Please pass comma separated list of 3rd party registries: ") 180 | *reg_names, _ = reader.ReadString('\n') 181 | } 182 | } 183 | 184 | regnames_list := strings.Split(stripSpaces(*reg_names), ",") 185 | for _, regitem := range regnames_list { 186 | if regitem == "GCR" || regitem == "gcr" { 187 | regurl := []string{"asia.gcr.io", "eu.gcr.io", "gcr.io", "marketplace.gcr.io", "staging-k8s.gcr.io", "us.gcr.io"} 188 | for _, gcreg := range regurl { 189 | sourceCluster.SetRegistry_Names ( strings.TrimSuffix(gcreg, "\n")) 190 | } 191 | } else if regitem == "DOCKERHUB" || regitem == "dockerhub" { 192 | regurl := "dockerhub" 193 | sourceCluster.SetRegistry_Names ( strings.TrimSuffix(regurl, "\n")) 194 | } else if regitem == "gitlab" || regitem == "GITLAB" { 195 | regurl := "registry.gitlab.com" 196 | sourceCluster.SetRegistry_Names ( strings.TrimSuffix(regurl, "\n")) 197 | } else if regitem == "mcr" || regitem == "MCR" { 198 | regurl := "mcr.microsoft.com" 199 | sourceCluster.SetRegistry_Names(strings.TrimSuffix(regurl, "\n")) 200 | } else { 201 | fmt.Println("Invalid registry name passed, exiting") 202 | os.Exit(4) 203 | } 204 | } 205 | 206 | *namespaces = strings.TrimSuffix(*namespaces, "\n") 207 | if *namespaces == "" { 208 | fmt.Println("Namespace value not passed, exiting") 209 | os.Exit(4) 210 | } 211 | if *namespaces != "all" { 212 | sourceCluster.SetNamespaces ( strings.Split(stripSpaces(*namespaces), ",") ) 213 | } 214 | 215 | *resources = strings.TrimSuffix(*resources, "\n") 216 | if *resources != "" { 217 | sourceCluster.SetResources ( strings.Split(stripSpaces(*resources), ",") ) 218 | destCluster.SetResources ( sourceCluster.GetResources() ) 219 | } 220 | 221 | // DESTINATION ============== 222 | if *destination_kubeconfig == "" { 223 | fmt.Print("Please pass the location of destination EKS cluster kubeconfig file: ") 224 | *destination_kubeconfig, _ = reader.ReadString('\n') 225 | } 226 | 227 | // get current destination context 228 | current_dst_context := get_current_context(strings.TrimSuffix(*destination_kubeconfig, "\n")) 229 | 230 | if *destination_context == "" { 231 | fmt.Printf("Please pass the destination context (default: %v): ", current_dst_context) 232 | *destination_context, _ = reader.ReadString('\n') 233 | } 234 | 235 | // Action for the tool 236 | if *action != "Deploy" && *action != "Delete" { 237 | fmt.Print("Please pass what action the tool needs to perform. Accepted values are Deploy or Delete : ") 238 | *action, _ = reader.ReadString('\n') 239 | *action = strings.TrimSuffix(*action, "\n") 240 | //fmt.Println("action entered", *action) 241 | if *action != "Deploy" && *action != "Delete" { 242 | fmt.Print("Invalid input for parameter \"action\", accepted values are Deploy or Delete") 243 | os.Exit(1) 244 | } 245 | } 246 | 247 | // fmt.Println("Action", *action) 248 | 249 | 250 | 251 | if *sourceType == "" { 252 | fmt.Print("Please pass source type (supported source types GKE,AKS,KOPS): ") 253 | *sourceType, _ = reader.ReadString('\n') 254 | *sourceType = strings.TrimRight(*sourceType, "\n") 255 | } 256 | 257 | destCluster.SetKubeconfig_path ( strings.TrimSuffix(*destination_kubeconfig, "\n") ) 258 | destCluster.SetContext ( strings.TrimSuffix(*destination_context, "\n") ) 259 | 260 | return sourceCluster, destCluster , *action, *sourceType 261 | } 262 | 263 | // Get default cluster in config 264 | func get_current_context(kubeconfigPath string) (default_context string) { 265 | kubeconfigPath = filepath.Clean(kubeconfigPath) 266 | source, err := ioutil.ReadFile(kubeconfigPath) 267 | if err != nil { 268 | fmt.Printf("Error opening up kube config file: %v\n", err) 269 | os.Exit(1) 270 | } 271 | 272 | err = yaml.Unmarshal(source, &config) 273 | if err != nil { 274 | fmt.Printf("Error getting default context from source config: %v\n", err) 275 | os.Exit(1) 276 | } 277 | 278 | default_context = config.CurrentContext 279 | 280 | return default_context 281 | } 282 | 283 | // helper function to drop spaces 284 | func stripSpaces(str string) string { 285 | return strings.Map(func(r rune) rune { 286 | if unicode.IsSpace(r) { 287 | // if the character is a space, drop it 288 | return -1 289 | } 290 | // else keep it in the string 291 | return r 292 | }, str) 293 | } 294 | 295 | func main() { 296 | 297 | reader := bufio.NewReader(os.Stdin) 298 | //accept user input 299 | sourceCluster, destCluster, action, sourceType := Get_user_input(reader) 300 | fmt.Println(action) 301 | 302 | /*Get connect with source and target clusters*/ 303 | 304 | //fmt.Println("sourceType:::", *sourceType) 305 | g := new(gke.GKE) 306 | a := new(aks.AKS) 307 | k := new(kops.KOPS) 308 | t := new(eks.EKS) 309 | var sourceResources resource.Resources 310 | target.SetContext(t,&destCluster) 311 | 312 | if sourceType == "GKE" { 313 | fmt.Println("GKE Resources") 314 | source.SetContext(g,&sourceCluster) 315 | sourceResources = source.Invoke( g , sourceType, &sourceCluster, &destCluster) 316 | // fmt.Println(sourceResources) 317 | } else if sourceType == "AKS" { 318 | source.SetContext(a,&sourceCluster) 319 | sourceResources = source.Invoke(a , sourceType, &sourceCluster, &destCluster ) 320 | // fmt.Println(sourceResources) 321 | } else if sourceType == "KOPS" { 322 | source.SetContext(k,&sourceCluster) 323 | sourceResources = source.Invoke(k, sourceType, &sourceCluster, &destCluster ) 324 | // fmt.Println(sourceResources) 325 | } else{ 326 | fmt.Println("Invalid input for parameter \"sourceType\", accepted values are GKE,AKE,KOPS") 327 | os.Exit(1) 328 | } 329 | target.Invoke(t,sourceType, &sourceCluster, &destCluster,&sourceResources, action) 330 | } 331 | --------------------------------------------------------------------------------