diff --git a/projects/aws/upgrader/ATTRIBUTION.txt b/projects/aws/upgrader/ATTRIBUTION.txt new file mode 100644 index 0000000000..42a9ec60a7 --- /dev/null +++ b/projects/aws/upgrader/ATTRIBUTION.txt @@ -0,0 +1,872 @@ + +** github.com/aws/eks-anywhere-build-tooling/projects/aws/upgrader; version 0.0.0 -- +https://github.com/aws/eks-anywhere-build-tooling/projects/aws/upgrader + +** github.com/aws/eks-anywhere-build-tooling/tools/version-tracker/pkg/util/logger; version v0.0.0-20240324014530-904f2adc3ec4 -- +https://github.com/aws/eks-anywhere-build-tooling/tools/version-tracker + +** github.com/go-logr/logr; version v1.3.0 -- +https://github.com/go-logr/logr + +** github.com/go-logr/zapr; version v1.2.4 -- +https://github.com/go-logr/zapr + +** github.com/spf13/afero; version v1.11.0 -- +https://github.com/spf13/afero + +** github.com/spf13/cobra; version v1.8.0 -- +https://github.com/spf13/cobra + +** gopkg.in/ini.v1; version v1.67.0 -- +https://gopkg.in/ini.v1 + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +------ + +** github.com/magiconair/properties; version v1.8.7 -- +https://github.com/magiconair/properties + +Copyright (c) 2013-2020, Frank Schroeder + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------ + +** github.com/fsnotify/fsnotify; version v1.7.0 -- +https://github.com/fsnotify/fsnotify + +Copyright © 2012 The Go Authors. All rights reserved. +Copyright © fsnotify Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +* Neither the name of Google Inc. nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------ + +** github.com/sagikazarmark/slog-shim; version v0.1.0 -- +https://github.com/sagikazarmark/slog-shim + +** golang.org/go; version go1.22.1 -- +https://github.com/golang/go + +** golang.org/x/sys/unix; version v0.16.0 -- +https://golang.org/x/sys + +** golang.org/x/text; version v0.14.0 -- +https://golang.org/x/text + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------ + +** github.com/spf13/pflag; version v1.0.5 -- +https://github.com/spf13/pflag + +Copyright (c) 2012 Alex Ogier. All rights reserved. +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------ + +** github.com/mitchellh/mapstructure; version v1.5.0 -- +https://github.com/mitchellh/mapstructure +Copyright (c) 2013 Mitchell Hashimoto + +** github.com/spf13/cast; version v1.6.0 -- +https://github.com/spf13/cast +Copyright (c) 2014 Steve Francia + +** github.com/spf13/viper; version v1.18.2 -- +https://github.com/spf13/viper +Copyright (c) 2014 Steve Francia + +** github.com/subosito/gotenv; version v1.6.0 -- +https://github.com/subosito/gotenv +Copyright (c) 2013 Alif Rachmawadi + +** go.uber.org/atomic; version v1.10.0 -- +https://github.com/uber-go/atomic +Copyright (c) 2016 Uber Technologies, Inc. + +** go.uber.org/multierr; version v1.9.0 -- +https://github.com/uber-go/multierr +Copyright (c) 2017-2021 Uber Technologies, Inc. + +** go.uber.org/zap; version v1.24.0 -- +https://github.com/uber-go/zap +Copyright (c) 2016-2017 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +------ + +** github.com/pelletier/go-toml/v2; version v2.1.0 -- +https://github.com/pelletier/go-toml/v2 +Copyright (c) 2021 - 2023 Thomas Pelletier + +go-toml v2 + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------ + +** gopkg.in/yaml.v3; version v3.0.1 -- +https://gopkg.in/yaml.v3 +Copyright (c) 2006-2010 Kirill Simonov +Copyright (c) 2006-2011 Kirill Simonov +Copyright (c) 2011-2019 Canonical Ltd + +This project is covered by two different licenses: MIT and Apache. + +#### MIT License #### + +The following files were ported to Go from C files of libyaml, and thus +are still covered by their original MIT license, with the additional +copyright staring in 2011 when the project was ported over: + +apic.go emitterc.go parserc.go readerc.go scannerc.go +writerc.go yamlh.go yamlprivateh.go + + + + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +### Apache License ### + +All the remaining project files are covered by the Apache license: + + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +* For gopkg.in/yaml.v3 see also this required NOTICE: +Copyright 2011-2016 Canonical Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +------ + +** github.com/hashicorp/hcl; version v1.0.0 -- +https://github.com/hashicorp/hcl + + * Package github.com/hashicorp/hcl's source code may be found at: + https://github.com/hashicorp/hcl/tree/v1.0.0 + +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. + + +------ diff --git a/projects/aws/upgrader/CHECKSUMS b/projects/aws/upgrader/CHECKSUMS new file mode 100644 index 0000000000..50cd3c214b --- /dev/null +++ b/projects/aws/upgrader/CHECKSUMS @@ -0,0 +1,2 @@ +e6567e9a36d45e49c6b700dc9d037cf1f4a97eb2e61f8a7b2a6bfde08d26fb4e _output/bin/upgrader/linux-amd64/upgrader +69ac857bf3b888240ff9d7dc455c312277fd18f44da4510e6d3b54065d8e34c2 _output/bin/upgrader/linux-arm64/upgrader diff --git a/projects/aws/upgrader/GOLANG_VERSION b/projects/aws/upgrader/GOLANG_VERSION new file mode 100644 index 0000000000..71f7f51df9 --- /dev/null +++ b/projects/aws/upgrader/GOLANG_VERSION @@ -0,0 +1 @@ +1.22 diff --git a/projects/aws/upgrader/Help.mk b/projects/aws/upgrader/Help.mk index a9c8c97d34..7e80689a88 100644 --- a/projects/aws/upgrader/Help.mk +++ b/projects/aws/upgrader/Help.mk @@ -6,6 +6,11 @@ # This is added to help document dynamic targets and support shell autocompletion +##@ Binary Targets +binaries: ## Build all binaries: `upgrader` for `linux/amd64 linux/arm64` +_output/bin/upgrader/linux-amd64/upgrader: ## Build `_output/bin/upgrader/linux-amd64/upgrader` +_output/bin/upgrader/linux-arm64/upgrader: ## Build `_output/bin/upgrader/linux-arm64/upgrader` + ##@ Image Targets local-images: ## Builds `upgrader/images/amd64` as oci tars for presumbit validation images: ## Pushes `upgrader/images/push` to IMAGE_REPO @@ -24,6 +29,11 @@ _output/1-26/dependencies/linux-arm64/eksa/containerd/containerd: ## Fetch `_out _output/1-26/dependencies/linux-amd64/eksa/kubernetes-sigs/cri-tools: ## Fetch `_output/1-26/dependencies/linux-amd64/eksa/kubernetes-sigs/cri-tools` _output/1-26/dependencies/linux-arm64/eksa/kubernetes-sigs/cri-tools: ## Fetch `_output/1-26/dependencies/linux-arm64/eksa/kubernetes-sigs/cri-tools` +##@ Checksum Targets +checksums: ## Update checksums file based on currently built binaries. +validate-checksums: # Validate checksums of currently built binaries against checksums file. +all-checksums: ## Update checksums files for all RELEASE_BRANCHes. + ##@ Run in Docker Targets run-in-docker/all-attributions: ## Run `all-attributions` in docker builder container run-in-docker/all-attributions-checksums: ## Run `all-attributions-checksums` in docker builder container @@ -35,6 +45,18 @@ run-in-docker/checksums: ## Run `checksums` in docker builder container run-in-docker/clean: ## Run `clean` in docker builder container run-in-docker/clean-go-cache: ## Run `clean-go-cache` in docker builder container run-in-docker/validate-checksums: ## Run `validate-checksums` in docker builder container +run-in-docker/upgrader/../eks-anywhere-go-mod-download: ## Run `upgrader/../eks-anywhere-go-mod-download` in docker builder container +run-in-docker/_output/bin/upgrader/linux-amd64/upgrader: ## Run `_output/bin/upgrader/linux-amd64/upgrader` in docker builder container +run-in-docker/_output/bin/upgrader/linux-arm64/upgrader: ## Run `_output/bin/upgrader/linux-arm64/upgrader` in docker builder container +run-in-docker/_output/attribution/go-license.csv: ## Run `_output/attribution/go-license.csv` in docker builder container + +##@ License Targets +gather-licenses: ## Helper to call $(GATHER_LICENSES_TARGETS) which gathers all licenses +attribution: ## Generates attribution from licenses gathered during `gather-licenses`. +attribution-pr: ## Generates PR to update attribution files for projects +attribution-checksums: ## Update attribution and checksums files. +all-attributions: ## Update attribution files for all RELEASE_BRANCHes. +all-attributions-checksums: ## Update attribution and checksums files for all RELEASE_BRANCHes. ##@ Clean Targets clean: ## Removes source and _output directory diff --git a/projects/aws/upgrader/LICENSE b/projects/aws/upgrader/LICENSE new file mode 100644 index 0000000000..67db858821 --- /dev/null +++ b/projects/aws/upgrader/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/projects/aws/upgrader/Makefile b/projects/aws/upgrader/Makefile index dadf50f5ae..6b908fb44a 100644 --- a/projects/aws/upgrader/Makefile +++ b/projects/aws/upgrader/Makefile @@ -1,4 +1,5 @@ -BASE_DIRECTORY:=$(shell git rev-parse --show-toplevel) +BASE_DIRECTORY:=$(shell git -C .. rev-parse --show-toplevel) +GOLANG_VERSION=$(shell cat GOLANG_VERSION) REPO=upgrader REPO_OWNER=aws @@ -7,11 +8,32 @@ GIT_TAG=v$(RELEASE_BRANCH)-$(shell yq e ".releases[] | select(.branch==\"${RELEA HAS_RELEASE_BRANCHES=true -EXCLUDE_FROM_CHECKSUMS_BUILDSPEC=true EXCLUDE_FROM_UPGRADE_BUILDSPEC=true # for the staging buildspec generation BUILDSPEC_DEPENDS_ON_OVERRIDE=containerd_containerd_linux_amd64 containerd_containerd_linux_arm64 kubernetes_sigs_cri_tools +# Go 1.18 and newer automatically embeds vcs information if the go module being built +# belongs (upwards) to a git repo. In this case, that repo is the buildtooling repo, +# which is problematic. Given these binaries are built from different banches, +# the local dev branch on PRs and `main` later, the VCS info will change between +# builds, making builds quite difficult to reproduce. That would require that the presubmits +# knew the future commit that might be created if the PR gets merged. +# Ideally this go module would live on its own repo, so all builds can be undoubtely +# linked to a particular commit of that repo and become independent from the build tooling repo. +# As an alternative, we don't include VCS info in the binary. +EXTRA_GOBUILD_FLAGS=-buildvcs=false + +BINARY_TARGET_FILES=upgrader +SOURCE_PATTERNS=. +GO_MOD_PATHS=.. + +# force binaries to go to non-release branch bin folder +BINARIES_ARE_RELEASE_BRANCHED=false +GIT_CHECKOUT_TARGET=main.go +FAKE_GIT_REPO_TARGET=.git +REPO_NO_CLONE=true + + BUILDSPECS=buildspec.yml buildspecs/combine-images.yml BUILDSPEC_1_COMPUTE_TYPE=BUILD_GENERAL1_LARGE BUILDSPEC_1_VARS_KEYS=IMAGE_PLATFORMS @@ -19,8 +41,6 @@ BUILDSPEC_1_VARS_VALUES=IMAGE_PLATFORMS BUILDSPEC_1_ARCH_TYPES=LINUX_CONTAINER ARM_CONTAINER BUILDSPEC_2_DEPENDS_ON_OVERRIDE=aws_upgrader_linux_amd64 aws_upgrader_linux_arm64 -REPO_NO_CLONE=true -HAS_LICENSES=false BASE_IMAGE_NAME=eks-distro-minimal-base-nsenter IMAGE_NAMES=upgrader @@ -30,14 +50,44 @@ DOCKERFILE_FOLDER=./docker/linux IMAGE_BUILD_ARGS= PROJECT_DEPENDENCIES=eksd/kubernetes/client eksd/kubernetes/server eksd/cni-plugins eksa/containerd/containerd eksa/kubernetes-sigs/cri-tools - +TOOLS_BIN_DIR=$(MAKE_ROOT)/hack/tools/bin +MOCKGEN=$(TOOLS_BIN_DIR)/mockgen +GO=build::common::use_go_version $(GOLANG_VERSION) include $(BASE_DIRECTORY)/Common.mk +build: unit-test + +$(REPO): + @mkdir $@ + +$(GATHER_LICENSES_TARGETS): | $(FAKE_GIT_REPO_TARGET) + +$(ATTRIBUTION_TARGETS): GIT_TAG + +$(FAKE_GIT_REPO_TARGET): + @git init + @git remote add origin https://github.com/aws/eks-anywhere-build-tooling.git + +$(TOOLS_BIN_DIR): + @mkdir -p $(TOOLS_BIN_DIR) + +$(MOCKGEN): $(TOOLS_BIN_DIR) + GOBIN=$(TOOLS_BIN_DIR) $(GO) install github.com/golang/mock/mockgen@v1.6.0 + +GIT_TAG: + echo 0.0.0 > $@ + +unit-test: | $$(ENABLE_DOCKER) + @$(GO) test ./... + +.PHONY: mocks +mocks: $(MOCKGEN) + $(GO) generate ./... ########### DO NOT EDIT ############################# # To update call: make add-generated-help-block # This is added to help document dynamic targets and support shell autocompletion # Run make help for a formatted help block with all targets include Help.mk -########### END GENERATED ########################### +########### END GENERATED ########################### \ No newline at end of file diff --git a/projects/aws/upgrader/cmd/root.go b/projects/aws/upgrader/cmd/root.go new file mode 100644 index 0000000000..6867eccb64 --- /dev/null +++ b/projects/aws/upgrader/cmd/root.go @@ -0,0 +1,53 @@ +package cmd + +import ( + "fmt" + "log" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spf13/viper" + + "github.com/aws/eks-anywhere-build-tooling/tools/version-tracker/pkg/util/logger" +) + +var rootCmd = &cobra.Command{ + Use: "upgrader", + Short: "EKS Anywhere InPlace upgrader", + Long: `Use EKS Anywhere InPlace upgrader to upgrade your nodes in place`, + PersistentPreRun: rootPersistentPreRun, +} + +func init() { + rootCmd.PersistentFlags().IntP("verbosity", "v", 0, "Set the log level verbosity") + if err := viper.BindPFlags(rootCmd.PersistentFlags()); err != nil { + log.Fatalf("failed to bind flags for root: %v", err) + } +} + +func prerunCmdBindFlags(cmd *cobra.Command, args []string) { + cmd.Flags().VisitAll(func(flag *pflag.Flag) { + err := viper.BindPFlag(flag.Name, flag) + if err != nil { + log.Fatalf("Error initializing flags: %v", err) + } + }) +} + +func rootPersistentPreRun(cmd *cobra.Command, args []string) { + if err := initLogger(); err != nil { + log.Fatalf("failed to init root command: %v", err) + } +} + +func initLogger() error { + if err := logger.Init(viper.GetInt("verbosity")); err != nil { + return fmt.Errorf("init zap logger: %v", err) + } + + return nil +} + +func Execute() error { + return rootCmd.Execute() +} diff --git a/projects/aws/upgrader/cmd/status.go b/projects/aws/upgrader/cmd/status.go new file mode 100644 index 0000000000..8851c182d4 --- /dev/null +++ b/projects/aws/upgrader/cmd/status.go @@ -0,0 +1,36 @@ +package cmd + +import ( + "context" + "fmt" + "log" + + "github.com/aws/eks-anywhere-build-tooling/projects/aws/upgrader/upgrade" + "github.com/spf13/cobra" +) + +var upgradeStatusCmd = &cobra.Command{ + Use: "status", + Short: "Upgrade status", + Long: "Use upgrade status command to get status of upgrade components on the node", + RunE: func(cmd *cobra.Command, args []string) error { + err := upgradeStatus(cmd.Context()) + if err != nil { + log.Fatalf("upgrade status failed: %v", err) + } + return nil + }, +} + +func init() { + upgradeCmd.AddCommand(upgradeStatusCmd) +} + +func upgradeStatus(ctx context.Context) error { + upg := upgrade.NewInPlaceUpgrader() + if err := upg.LogStatusAndCleanup(ctx); err != nil { + return fmt.Errorf("fetching upgrade status on the node: %v", err) + } + + return nil +} diff --git a/projects/aws/upgrader/cmd/upgrade.go b/projects/aws/upgrader/cmd/upgrade.go new file mode 100644 index 0000000000..d1a312495a --- /dev/null +++ b/projects/aws/upgrader/cmd/upgrade.go @@ -0,0 +1,15 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +var upgradeCmd = &cobra.Command{ + Use: "upgrade", + Short: "Upgrade command", + Long: "Use upgrade command to run different upgrade commands on the node", +} + +func init() { + rootCmd.AddCommand(upgradeCmd) +} diff --git a/projects/aws/upgrader/cmd/upgradecniplugins.go b/projects/aws/upgrader/cmd/upgradecniplugins.go new file mode 100644 index 0000000000..bbe5319542 --- /dev/null +++ b/projects/aws/upgrader/cmd/upgradecniplugins.go @@ -0,0 +1,35 @@ +package cmd + +import ( + "context" + "fmt" + "log" + + "github.com/aws/eks-anywhere-build-tooling/projects/aws/upgrader/upgrade" + "github.com/spf13/cobra" +) + +var upgradeCNIPluginsCmd = &cobra.Command{ + Use: "cni-plugins", + Short: "Upgrade cni-plugins", + Long: "Use upgrade cni-plugins command to upgrade cni plugins on the node", + RunE: func(cmd *cobra.Command, args []string) error { + err := upgradeCNIPlugins(cmd.Context()) + if err != nil { + log.Fatalf("upgrade cni-plugins failed: %v", err) + } + return nil + }, +} + +func init() { + upgradeCmd.AddCommand(upgradeCNIPluginsCmd) +} + +func upgradeCNIPlugins(ctx context.Context) error { + upg := upgrade.NewInPlaceUpgrader() + if err := upg.CNIPluginsUpgrade(ctx); err != nil { + return fmt.Errorf("upgrading cni-plugins on the node: %v", err) + } + return nil +} diff --git a/projects/aws/upgrader/cmd/upgradecontainerd.go b/projects/aws/upgrader/cmd/upgradecontainerd.go new file mode 100644 index 0000000000..24a42c2a4c --- /dev/null +++ b/projects/aws/upgrader/cmd/upgradecontainerd.go @@ -0,0 +1,36 @@ +package cmd + +import ( + "context" + "fmt" + "log" + + "github.com/aws/eks-anywhere-build-tooling/projects/aws/upgrader/upgrade" + "github.com/spf13/cobra" +) + +var upgradeContainerdCmd = &cobra.Command{ + Use: "containerd", + Short: "Upgrade containerd", + Long: "Use upgrade containerd command to upgrade containerd on the node", + RunE: func(cmd *cobra.Command, args []string) error { + err := upgradeContainerd(cmd.Context()) + if err != nil { + log.Fatalf("upgrade containerd failed: %v", err) + } + return nil + }, +} + +func init() { + upgradeCmd.AddCommand(upgradeContainerdCmd) +} + +func upgradeContainerd(ctx context.Context) error { + upg := upgrade.NewInPlaceUpgrader() + if err := upg.ContainerdUpgrade(ctx); err != nil { + return fmt.Errorf("upgrading containerd on node: %v", err) + } + + return nil +} diff --git a/projects/aws/upgrader/cmd/upgradekubeletkubectl.go b/projects/aws/upgrader/cmd/upgradekubeletkubectl.go new file mode 100644 index 0000000000..95b843f0f8 --- /dev/null +++ b/projects/aws/upgrader/cmd/upgradekubeletkubectl.go @@ -0,0 +1,36 @@ +package cmd + +import ( + "context" + "fmt" + "log" + + "github.com/aws/eks-anywhere-build-tooling/projects/aws/upgrader/upgrade" + "github.com/spf13/cobra" +) + +var upgradeKubeletKubectlCmd = &cobra.Command{ + Use: "kubelet-kubectl", + Short: "Upgrade kubelet-kubectl", + Long: "Use upgrade kubelet-kubectl command to upgrade kubelet and kubectl on the node", + RunE: func(cmd *cobra.Command, args []string) error { + err := upgradeKubeletAndKubectl(cmd.Context()) + if err != nil { + log.Fatalf("upgrade kubelet-kubectl failed: %v", err) + } + return nil + }, +} + +func init() { + upgradeCmd.AddCommand(upgradeKubeletKubectlCmd) +} + +func upgradeKubeletAndKubectl(ctx context.Context) error { + upg := upgrade.NewInPlaceUpgrader() + if err := upg.KubeletKubectlUpgrade(ctx); err != nil { + return fmt.Errorf("upgrading kubelet and kubectl on node: %v", err) + } + + return nil +} diff --git a/projects/aws/upgrader/cmd/upgradenode.go b/projects/aws/upgrader/cmd/upgradenode.go new file mode 100644 index 0000000000..c9e4912343 --- /dev/null +++ b/projects/aws/upgrader/cmd/upgradenode.go @@ -0,0 +1,75 @@ +package cmd + +import ( + "context" + "errors" + "fmt" + "log" + + "github.com/aws/eks-anywhere-build-tooling/projects/aws/upgrader/upgrade" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var upgradeNodeCmd = &cobra.Command{ + Use: "node", + Short: "Upgrade node", + Long: "Use upgrade node command to upgrade kubeadm on the node", + RunE: func(cmd *cobra.Command, args []string) error { + var err error + + nodeType := viper.GetString("type") + k8sVersion := viper.GetString("k8sVersion") + etcdVersion := viper.GetString("etcdVersion") + if etcdVersion == "" { + etcdVersion = "NO_UPDATE" + } + + err = upgradeNode(cmd.Context(), nodeType, k8sVersion, etcdVersion) + if err != nil { + log.Fatalf("upgrade node failed: %v", err) + } + return nil + }, +} + +func init() { + var err error + + upgradeCmd.AddCommand(upgradeNodeCmd) + upgradeNodeCmd.Flags().String("type", "", "Node type flag") + upgradeNodeCmd.Flags().String("k8sVersion", "", "kubernetes version flag") + upgradeNodeCmd.Flags().String("etcdVersion", "", "etcd version flag") + err = viper.BindPFlags(upgradeNodeCmd.Flags()) + if err != nil { + log.Fatalf("Error initializing flags: %v", err) + } +} + +func upgradeNode(ctx context.Context, nodeType, k8sVersion, etcdVersion string) error { + upg := upgrade.NewInPlaceUpgrader(upgrade.WithKubernetesVersion(k8sVersion), upgrade.WithEtcdVersion(etcdVersion)) + + switch nodeType { + case "FirstCP": + if k8sVersion == "" { + return errors.New("k8sVersion flag has to be set for first control plane node") + } + + if err := upg.KubeAdmInFirstCP(ctx); err != nil { + return fmt.Errorf("upgrading kubeadm in first controlplane node: %v", err) + } + + case "RestCP": + if err := upg.KubeAdmInRestCP(ctx); err != nil { + return fmt.Errorf("upgrading kubeadm in controlplane node: %v", err) + } + case "Worker": + if err := upg.KubeAdmInWorker(ctx); err != nil { + return fmt.Errorf("upgrading kubeadm in worker node: %v", err) + } + default: + return fmt.Errorf("invalid node type, please specify one of the three types: FirstCP, RestCP or Worker") + } + + return nil +} diff --git a/projects/aws/upgrader/docker/linux/Dockerfile b/projects/aws/upgrader/docker/linux/Dockerfile index 1e83098144..5fa8b699b4 100644 --- a/projects/aws/upgrader/docker/linux/Dockerfile +++ b/projects/aws/upgrader/docker/linux/Dockerfile @@ -22,6 +22,7 @@ COPY _output/$RELEASE_BRANCH/dependencies/$TARGETOS-$TARGETARCH/eksd/kubernetes/ _output/$RELEASE_BRANCH/dependencies/$TARGETOS-$TARGETARCH/eksd/kubernetes/server/bin/kubeadm \ /eksa-upgrades/binaries/kubernetes/usr/bin/ COPY ../../upgrade.sh /eksa-upgrades/scripts/ +COPY _output/bin/upgrader/$TARGETOS-$TARGETARCH/upgrader /eksa-upgrades/tools/ COPY _output/$RELEASE_BRANCH/dependencies/$TARGETOS-$TARGETARCH/eksa/containerd/containerd/usr/local/bin/CONTAINERD_ATTRIBUTION.txt /THIRD_PARTY_LICENSES/CONTAINERD_ATTRIBUTION.txt COPY _output/$RELEASE_BRANCH/dependencies/$TARGETOS-$TARGETARCH/eksa/containerd/containerd/usr/local/sbin/RUNC_ATTRIBUTION.txt /THIRD_PARTY_LICENSES/RUNC_ATTRIBUTION.txt @@ -31,3 +32,5 @@ COPY _output/$RELEASE_BRANCH/dependencies/$TARGETOS-$TARGETARCH/eksd/cni-plugins COPY _output/$RELEASE_BRANCH/dependencies/$TARGETOS-$TARGETARCH/eksd/cni-plugins/LICENSES /THIRD_PARTY_LICENSES/CNI-PLUGINS_LICENSES COPY _output/$RELEASE_BRANCH/dependencies/$TARGETOS-$TARGETARCH/eksd/kubernetes/ATTRIBUTION.txt /THIRD_PARTY_LICENSES/KUBERNETES_ATTRIBUTION.txt COPY _output/$RELEASE_BRANCH/dependencies/$TARGETOS-$TARGETARCH/eksd/kubernetes/LICENSES /THIRD_PARTY_LICENSES/KUBERNETES_LICENSES +COPY ATTRIBUTION.txt /ATTRIBUTION.txt +COPY _output/LICENSES /LICENSES diff --git a/projects/aws/upgrader/go.mod b/projects/aws/upgrader/go.mod new file mode 100644 index 0000000000..b11c461ffe --- /dev/null +++ b/projects/aws/upgrader/go.mod @@ -0,0 +1,39 @@ +module github.com/aws/eks-anywhere-build-tooling/projects/aws/upgrader + +go 1.22 + +require ( + github.com/aws/eks-anywhere-build-tooling/tools/version-tracker v0.0.0-20240324014530-904f2adc3ec4 + github.com/golang/mock v1.6.0 + github.com/onsi/gomega v1.32.0 + github.com/spf13/cobra v1.8.0 + github.com/spf13/pflag v1.0.5 + github.com/spf13/viper v1.18.2 +) + +require ( + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/zapr v1.2.4 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + go.uber.org/zap v1.24.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/projects/aws/upgrader/go.sum b/projects/aws/upgrader/go.sum new file mode 100644 index 0000000000..ba6cf28a87 --- /dev/null +++ b/projects/aws/upgrader/go.sum @@ -0,0 +1,139 @@ +github.com/aws/eks-anywhere-build-tooling/tools/version-tracker v0.0.0-20240324014530-904f2adc3ec4 h1:zcT7pEqyeI8DNwlXrwuLyjF98LTUMY51i8NR2vnuNqw= +github.com/aws/eks-anywhere-build-tooling/tools/version-tracker v0.0.0-20240324014530-904f2adc3ec4/go.mod h1:F1TzNzFZPJ5shewblO41Pbk2hyTqVa04WdtCizetlWo= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= +github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= +github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= +github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= +github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/projects/aws/upgrader/main.go b/projects/aws/upgrader/main.go new file mode 100644 index 0000000000..0d50239647 --- /dev/null +++ b/projects/aws/upgrader/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "os" + + "github.com/aws/eks-anywhere-build-tooling/projects/aws/upgrader/cmd" +) + +func main() { + if cmd.Execute() == nil { + os.Exit(0) + } + os.Exit(-1) +} diff --git a/projects/aws/upgrader/upgrade/cniplugins.go b/projects/aws/upgrader/upgrade/cniplugins.go new file mode 100644 index 0000000000..7f671d6e5a --- /dev/null +++ b/projects/aws/upgrader/upgrade/cniplugins.go @@ -0,0 +1,38 @@ +package upgrade + +import ( + "context" + "fmt" + + "github.com/aws/eks-anywhere-build-tooling/tools/version-tracker/pkg/util/logger" +) + +// CNIPluginsUpgrade upgrades cni plugins on the node +func (u *InPlaceUpgrader) CNIPluginsUpgrade(ctx context.Context) error { + cmpDir, err := u.upgradeComponentsBinDir() + if err != nil { + return fmt.Errorf("getting upgrade components binary directory: %v", err) + } + + cniVersionCmd := []string{"/opt/cni/bin/loopback", "--version"} + out, err := u.ExecCommand(ctx, cniVersionCmd[0], cniVersionCmd[1:]...) + if err != nil { + return execError(cniVersionCmd, string(out)) + } + logger.Info("current version of cni plugins", "version", string(out)) + + cpCmd := []string{"cp", "-rf", fmt.Sprintf("%s/cni-plugins/.", cmpDir), "/"} + out, err = u.ExecCommand(ctx, cpCmd[0], cpCmd[1:]...) + if err != nil { + return execError(cpCmd, string(out)) + } + + out, err = u.ExecCommand(ctx, cniVersionCmd[0], cniVersionCmd[1:]...) + if err != nil { + return execError(cniVersionCmd, string(out)) + } + + logger.Info("cni plugins version on the node", "version", string(out)) + logger.Info("cni plugins upgrade successful!") + return nil +} diff --git a/projects/aws/upgrader/upgrade/cniplugins_test.go b/projects/aws/upgrader/upgrade/cniplugins_test.go new file mode 100644 index 0000000000..22614e3728 --- /dev/null +++ b/projects/aws/upgrader/upgrade/cniplugins_test.go @@ -0,0 +1,68 @@ +package upgrade_test + +import ( + "context" + "errors" + "fmt" + "testing" + + . "github.com/onsi/gomega" +) + +const ( + cmpDir = "/foo/binaries" +) + +func TestCNIPluginsUpgrade(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "/opt/cni/bin/loopback", "--version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "cp", "-rf", fmt.Sprintf("%s/cni-plugins/.", cmpDir), "/").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "/opt/cni/bin/loopback", "--version").Return(nil, nil).Times(1) + + err := tt.u.CNIPluginsUpgrade(ctx) + tt.Expect(err).To(BeNil()) +} + +func TestCNIPluginsUpgradeComponentsDirError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("", errors.New("")).Times(1) + + err := tt.u.CNIPluginsUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestCNIPluginsUpgradeFirstVersionCmdError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "/opt/cni/bin/loopback", "--version").Return(nil, errors.New("")).Times(1) + + err := tt.u.CNIPluginsUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestCNIPluginsUpgradeRecursiveCopyError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "/opt/cni/bin/loopback", "--version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "cp", "-rf", fmt.Sprintf("%s/cni-plugins/.", cmpDir), "/").Return(nil, errors.New("")).Times(1) + + err := tt.u.CNIPluginsUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestCNIPluginsUpgradeSecondVersionCmdError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "/opt/cni/bin/loopback", "--version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "cp", "-rf", fmt.Sprintf("%s/cni-plugins/.", cmpDir), "/").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "/opt/cni/bin/loopback", "--version").Return(nil, errors.New("")).Times(1) + + err := tt.u.CNIPluginsUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} diff --git a/projects/aws/upgrader/upgrade/containerd.go b/projects/aws/upgrader/upgrade/containerd.go new file mode 100644 index 0000000000..8e71e3885e --- /dev/null +++ b/projects/aws/upgrader/upgrade/containerd.go @@ -0,0 +1,53 @@ +package upgrade + +import ( + "context" + "fmt" + + "github.com/aws/eks-anywhere-build-tooling/tools/version-tracker/pkg/util/logger" +) + +// ContainerdUpgrade upgrades containerd on the node +// As part of the upgrade: +// 1. copy over the new containerd components +// 2. reload the daemon and restart containerd with the new version +func (u *InPlaceUpgrader) ContainerdUpgrade(ctx context.Context) error { + cmpDir, err := u.upgradeComponentsBinDir() + if err != nil { + return fmt.Errorf("getting upgrade components binary directory: %v", err) + } + + containerdVersionCmd := []string{"containerd", "--version"} + out, err := u.ExecCommand(ctx, containerdVersionCmd[0], containerdVersionCmd[1:]...) + if err != nil { + return execError(containerdVersionCmd, string(out)) + } + logger.Info("current version of containerd", "version", string(out)) + + cpCmd := []string{"cp", "-rf", fmt.Sprintf("%s/containerd/.", cmpDir), "/"} + out, err = u.ExecCommand(ctx, cpCmd[0], cpCmd[1:]...) + if err != nil { + return execError(cpCmd, string(out)) + } + + version, err := u.ExecCommand(ctx, containerdVersionCmd[0], containerdVersionCmd[1:]...) + if err != nil { + return execError(containerdVersionCmd, string(version)) + } + + daemonReloadCmd := []string{"systemctl", "daemon-reload"} + out, err = u.ExecCommand(ctx, daemonReloadCmd[0], daemonReloadCmd[1:]...) + if err != nil { + return execError(daemonReloadCmd, string(out)) + } + + containerdRestartCmd := []string{"systemctl", "restart", "containerd"} + out, err = u.ExecCommand(ctx, containerdRestartCmd[0], containerdRestartCmd[1:]...) + if err != nil { + return execError(containerdRestartCmd, string(out)) + } + + logger.Info("containerd version on the node", "version", string(version)) + logger.Info("containerd upgrade successful!") + return nil +} diff --git a/projects/aws/upgrader/upgrade/containerd_test.go b/projects/aws/upgrader/upgrade/containerd_test.go new file mode 100644 index 0000000000..601594f06c --- /dev/null +++ b/projects/aws/upgrader/upgrade/containerd_test.go @@ -0,0 +1,93 @@ +package upgrade_test + +import ( + "context" + "errors" + "fmt" + "testing" + + . "github.com/onsi/gomega" +) + +func TestContainerdUpgrade(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "containerd", "--version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "cp", "-rf", fmt.Sprintf("%s/containerd/.", cmpDir), "/").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "containerd", "--version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "daemon-reload").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "restart", "containerd").Return(nil, nil).Times(1) + + err := tt.u.ContainerdUpgrade(ctx) + tt.Expect(err).To(BeNil()) +} + +func TestContainerdUpgradeComponentsDirError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("", errors.New("")).Times(1) + + err := tt.u.ContainerdUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestContainerdUpgradeFirstVersionCmdError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "containerd", "--version").Return(nil, errors.New("")).Times(1) + + err := tt.u.ContainerdUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestContainerdUpgradeRecursiveCopyError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "containerd", "--version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "cp", "-rf", fmt.Sprintf("%s/containerd/.", cmpDir), "/").Return(nil, errors.New("")).Times(1) + + err := tt.u.ContainerdUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestContainerdUpgradeSecondVersionCmdError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "containerd", "--version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "cp", "-rf", fmt.Sprintf("%s/containerd/.", cmpDir), "/").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "containerd", "--version").Return(nil, errors.New("")).Times(1) + + err := tt.u.ContainerdUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestContainerdUpgradeDaemonReloadError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "containerd", "--version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "cp", "-rf", fmt.Sprintf("%s/containerd/.", cmpDir), "/").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "containerd", "--version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "daemon-reload").Return(nil, errors.New("")).Times(1) + + err := tt.u.ContainerdUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestContainerdUpgradeContainerdRestartError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "containerd", "--version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "cp", "-rf", fmt.Sprintf("%s/containerd/.", cmpDir), "/").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "containerd", "--version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "daemon-reload").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "restart", "containerd").Return(nil, errors.New("")).Times(1) + + err := tt.u.ContainerdUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} diff --git a/projects/aws/upgrader/upgrade/kubeadm.go b/projects/aws/upgrader/upgrade/kubeadm.go new file mode 100644 index 0000000000..ae1c75c162 --- /dev/null +++ b/projects/aws/upgrader/upgrade/kubeadm.go @@ -0,0 +1,295 @@ +package upgrade + +import ( + "context" + "fmt" + "strings" + + "github.com/aws/eks-anywhere-build-tooling/tools/version-tracker/pkg/util/logger" +) + +const ( + kubeAdmBinDir = "/usr/bin/kubeadm" + etcdImageRepo = "public.ecr.aws/eks-distro/etcd-io" + noEtcdUpdate = "NO_UPDATE" + yamlSeparatorWithNewLine = "---\n" + staticKubeVipPath = "/etc/kubernetes/manifests/kube-vip.yaml" + kubeConfigPath = "/etc/kubernetes/admin.conf" +) + +// KubeAdmInFirstCP upgrades the first control plane node +// +// As part of upgrade: +// 1. backs up the existing kubeadm binary, replace with the new binary and backs up existing kubeadm cluster-config. +// 2. updates the cluster config with latest etcd version and appends the kubelet config. +// 3. back up and delete coreDNS config as capi handles coreDNS upgrade. +// 4. run kubadm upgrade commands and copy over new kube-vip config. +// 5. Restore coreDNS config back once the kubeadm upgrade commands are complete. +func (u *InPlaceUpgrader) KubeAdmInFirstCP(ctx context.Context) error { + componentsDir, err := u.upgradeComponentsKubernetesBinDir() + if err != nil { + return fmt.Errorf("getting upgrade components kubernetes binary directory: %v", err) + } + + if err = u.BackUpAndReplace(kubeAdmBinDir, componentsDir, fmt.Sprintf("%s/kubeadm", componentsDir)); err != nil { + return fmt.Errorf("backing up and replacing kubeadm binary: %v", err) + } + logger.Info("backed up and replaced kubeadm binary successfully") + + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", componentsDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", componentsDir) + + getClusterConfigCmd := []string{"kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath} + out, err := u.ExecCommand(ctx, getClusterConfigCmd[0], getClusterConfigCmd[1:]...) + if err != nil { + return execError(getClusterConfigCmd, string(out)) + } + + trimmedOut := strings.Trim(string(out), "`'") + err = u.WriteFile(kubeAdmConfigBackUp, []byte(trimmedOut), fileMode640) + if err != nil { + return fmt.Errorf("writing kubeadm config to backup file: %v", err) + } + + if u.etcdVersion != noEtcdUpdate { + if err = u.updateEtcdVersion(kubeAdmConfigBackUp, newKubeAdmConfig, u.etcdVersion); err != nil { + return fmt.Errorf("updating etcd version to %s: %v", u.etcdVersion, err) + } + } + + if err = u.appendKubeletConfig(ctx, newKubeAdmConfig); err != nil { + return fmt.Errorf("appending kubelet config: %v", err) + } + + if err = u.backUpAndDeleteCoreDNSConfig(ctx, componentsDir); err != nil { + return fmt.Errorf("backing up and deleting coreDNS config: %v", err) + } + + kubeAdmVersionCmd := []string{"kubeadm", "version"} + version, err := u.ExecCommand(ctx, kubeAdmVersionCmd[0], kubeAdmVersionCmd[1:]...) + if err != nil { + return execError(kubeAdmVersionCmd, string(version)) + } + logger.Info("current version of kubeadm", "cmd", "kubeadm version", "output", string(version)) + + kubeAdmUpgPlanCmd := []string{"kubeadm", "upgrade", "plan", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig} + kubeAdmUpgPlan, err := u.ExecCommand(ctx, kubeAdmUpgPlanCmd[0], kubeAdmUpgPlanCmd[1:]...) + if err != nil { + return execError(kubeAdmUpgPlanCmd, string(kubeAdmUpgPlan)) + } + logger.Info("components to be upgraded with kubeadm", "output", string(kubeAdmUpgPlan)) + + kubeAdmUpgCmd := []string{"kubeadm", "upgrade", "apply", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig, "--allow-experimental-upgrades", "--yes"} + kubeAdmUpg, err := u.ExecCommand(ctx, kubeAdmUpgCmd[0], kubeAdmUpgCmd[1:]...) + if err != nil { + return execError(kubeAdmUpgCmd, string(kubeAdmUpg)) + } + logger.Info("verbose output for kubeadm upgrade", "output", string(kubeAdmUpg)) + + upgCmpDir, err := u.upgradeComponentsDir() + if err != nil { + return fmt.Errorf("getting upgrade components directory: %v", err) + } + + newKubeVipConfigPath := fmt.Sprintf("%s/kube-vip.yaml", upgCmpDir) + if err := u.copy(staticKubeVipPath, fmt.Sprintf("%s/kube-vip.backup.yaml", upgCmpDir)); err != nil { + return copyError(staticKubeVipPath, fmt.Sprintf("%s/kube-vip.backup.yaml", upgCmpDir), err) + } + + if err := u.copy(newKubeVipConfigPath, staticKubeVipPath); err != nil { + return copyError(newKubeVipConfigPath, staticKubeVipPath, err) + } + + if err = u.restoreCoreDNSConfig(ctx, componentsDir); err != nil { + return fmt.Errorf("restoring coreDNS config: %v", err) + } + logger.Info("kubeadm upgrade in first control plane successful!", "version", u.kubernetesVersion) + + return nil +} + +// KubeAdmInRestCP upgrades the rest of control plane nodes +// +// As part of upgrade: +// 1. backs up the existing kubeadm binary and replace with the new binary. +// 2. back up and delete coreDNS config as capi handles coreDNS upgrade. +// 3. run kubadm upgrade commands and copy over new kube-vip config. +// 4. Restore coreDNS config back once the kubeadm upgrade commands are complete. +func (u *InPlaceUpgrader) KubeAdmInRestCP(ctx context.Context) error { + componentsDir, err := u.upgradeComponentsKubernetesBinDir() + if err != nil { + return fmt.Errorf("getting upgrade components kubernetes binary directory: %v", err) + } + + if err = u.BackUpAndReplace(kubeAdmBinDir, componentsDir, fmt.Sprintf("%s/kubeadm", componentsDir)); err != nil { + return fmt.Errorf("backing up and replacing kubeadm binary: %v", err) + } + logger.Info("Backed up and replaced kubeadm binary successfully") + + if err = u.backUpAndDeleteCoreDNSConfig(ctx, componentsDir); err != nil { + return fmt.Errorf("backing up and deleting coreDNS config: %v", err) + } + + kubeAdmVersionCmd := []string{"kubeadm", "version"} + version, err := u.ExecCommand(ctx, kubeAdmVersionCmd[0], kubeAdmVersionCmd[1:]...) + if err != nil { + return execError(kubeAdmVersionCmd, string(version)) + } + logger.Info("current version of kubeadm", "cmd", "kubeadm version", "output", string(version)) + + kubeAdmUpgNodeCmd := []string{"kubeadm", "upgrade", "node", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration"} + kubeAdmUpg, err := u.ExecCommand(ctx, kubeAdmUpgNodeCmd[0], kubeAdmUpgNodeCmd[1:]...) + if err != nil { + return execError(kubeAdmUpgNodeCmd, string(kubeAdmUpg)) + } + logger.Info("verbose output for kubeadm upgrade", "output", string(kubeAdmUpg)) + + upgCmpDir, err := u.upgradeComponentsDir() + if err != nil { + return fmt.Errorf("getting upgrade components directory: %v", err) + } + + newKubeVipConfigPath := fmt.Sprintf("%s/kube-vip.yaml", upgCmpDir) + if err := u.copy(staticKubeVipPath, fmt.Sprintf("%s/kube-vip.backup.yaml", upgCmpDir)); err != nil { + return copyError(staticKubeVipPath, fmt.Sprintf("%s/kube-vip.backup.yaml", upgCmpDir), err) + } + + if err := u.copy(newKubeVipConfigPath, staticKubeVipPath); err != nil { + return copyError(newKubeVipConfigPath, staticKubeVipPath, err) + } + + if err = u.restoreCoreDNSConfig(ctx, componentsDir); err != nil { + return fmt.Errorf("restoring coreDNS config: %v", err) + } + logger.Info("kubeadm upgrade in control plane successful!") + + return nil +} + +// KubeAdmInWorker upgrades the worker nodes +// +// As part of upgrade: +// 1. backs up the existing kubeadm binary and replace with the new binary. +// 2. run kubadm upgrade commands and copy over new kube-vip config. +func (u *InPlaceUpgrader) KubeAdmInWorker(ctx context.Context) error { + componentsDir, err := u.upgradeComponentsKubernetesBinDir() + if err != nil { + return fmt.Errorf("getting upgrade components kubernetes binary directory: %v", err) + } + + if err = u.BackUpAndReplace(kubeAdmBinDir, componentsDir, fmt.Sprintf("%s/kubeadm", componentsDir)); err != nil { + return fmt.Errorf("backing up and replacing kubeadm binary: %v", err) + } + logger.Info("Backed up and replaced kubeadm binary successfully") + + kubeAdmVersionCmd := []string{"kubeadm", "version"} + version, err := u.ExecCommand(ctx, kubeAdmVersionCmd[0], kubeAdmVersionCmd[1:]...) + if err != nil { + return execError(kubeAdmVersionCmd, string(version)) + } + logger.Info("current version of kubeadm", "cmd", "kubeadm version", "output", string(version)) + + kubeAdmUpgNodeCmd := []string{"kubeadm", "upgrade", "node"} + kubeAdmUpg, err := u.ExecCommand(ctx, kubeAdmUpgNodeCmd[0], kubeAdmUpgNodeCmd[1:]...) + if err != nil { + return execError(kubeAdmUpgNodeCmd, string(kubeAdmUpg)) + } + logger.Info("verbose output for kubeadm upgrade", "output", string(kubeAdmUpg)) + logger.Info("kubeadm upgrade in worker node successful!") + + return nil +} + +// updateEtcdVersion updates the etcd image to the latest tag for that kubernetes version in the kubeadm config file +func (u *InPlaceUpgrader) updateEtcdVersion(oldKubeAdmConf, newKubeAdmConf, version string) error { + conf, err := u.ReadFile(oldKubeAdmConf) + if err != nil { + return err + } + lines := strings.Split(string(conf), "\n") + for i, line := range lines { + if strings.Contains(line, etcdImageRepo) { + imageTag := strings.Split(lines[i+1], ":") + // the space in the below string is for yaml formatting and should not be removed + imageTag[1] = fmt.Sprintf(" %s", version) + lines[i+1] = strings.Join(imageTag, ":") + } + } + updatedConf := strings.Join(lines, "\n") + err = u.WriteFile(newKubeAdmConf, []byte(updatedConf), fileMode640) + if err != nil { + return err + } + return nil +} + +// appendKubeletConfig retreives kubelet-config and appends it to the existing kubeadm-config file +func (u *InPlaceUpgrader) appendKubeletConfig(ctx context.Context, kubeAdmConf string) error { + conf, err := u.ReadFile(kubeAdmConf) + if err != nil { + return err + } + conf = append(conf, []byte(yamlSeparatorWithNewLine)...) + getKubeletConfCmd := []string{"kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath} + out, err := u.ExecCommand(ctx, getKubeletConfCmd[0], getKubeletConfCmd[1:]...) + if err != nil { + return execError(getKubeletConfCmd, string(out)) + } + logger.Info("kubelet config as string", "out", string(out)) + + trimmedOut := strings.Trim(string(out), "`'") + conf = append(conf, []byte(trimmedOut)...) + err = u.WriteFile(kubeAdmConf, []byte(conf), fileMode640) + if err != nil { + return fmt.Errorf("writing kubelet config to kubeadm file: %v", err) + } + + logger.Info("appended kubelet config to kubeadm config file") + return nil +} + +// backUpAndDeleteCoreDNSConfig backs up and deletes the coreDNS config map. +// Backup and delete coredns configmap. If the CM doesn't exist, kubeadm will skip its upgrade. +// This is desirable for 2 reasons: +// 1. CAPI already takes care of coredns upgrades +// 2. kubeadm will fail when verifying the current version of coredns bc the image tag created by eks-a +// is not recognised by the migration verification logic https://github.com/coredns/corefile-migration/blob/master/migration/versions.go +// +// Ideally we will instruct kubeadm to just skip coredns upgrade during this phase, but +// it doesn't seem like there is an option. +// TODO: consider using --skip-phases to skip addons/coredns once the feature flag is supported in kubeadm upgrade command +func (u *InPlaceUpgrader) backUpAndDeleteCoreDNSConfig(ctx context.Context, cmpDir string) error { + coreDNSBackup := fmt.Sprintf("%s/coredns.yaml", cmpDir) + getCoreDNSConfCmd := []string{"kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true"} + coreDNSConf, err := u.ExecCommand(ctx, getCoreDNSConfCmd[0], getCoreDNSConfCmd[1:]...) + if err != nil { + return execError(getCoreDNSConfCmd, string(coreDNSConf)) + } + if len(coreDNSConf) > 0 { + logger.Info("coreDNS config as string", "out", string(coreDNSConf)) + err = u.WriteFile(coreDNSBackup, coreDNSConf, fileMode640) + if err != nil { + return err + } + } + deleteCoreDNSConfig := []string{"kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true"} + out, err := u.ExecCommand(ctx, deleteCoreDNSConfig[0], deleteCoreDNSConfig[1:]...) + if err != nil { + return execError(deleteCoreDNSConfig, string(out)) + } + + logger.Info("backed up and deleted coreDNS config") + return nil +} + +func (u *InPlaceUpgrader) restoreCoreDNSConfig(ctx context.Context, cmpDir string) error { + coreDNSBackup := fmt.Sprintf("%s/coredns.yaml", cmpDir) + createCoreDNSConfCmd := []string{"kubectl", "create", "-f", coreDNSBackup, "--kubeconfig", kubeConfigPath} + out, err := u.ExecCommand(ctx, createCoreDNSConfCmd[0], createCoreDNSConfCmd[1:]...) + if err != nil { + return execError(createCoreDNSConfCmd, string(out)) + } + + logger.Info("restored coreDNS config successfully!") + return nil +} diff --git a/projects/aws/upgrader/upgrade/kubeadm_test.go b/projects/aws/upgrader/upgrade/kubeadm_test.go new file mode 100644 index 0000000000..fe64d90e96 --- /dev/null +++ b/projects/aws/upgrader/upgrade/kubeadm_test.go @@ -0,0 +1,1003 @@ +package upgrade_test + +import ( + "context" + "errors" + "fmt" + "testing" + + upgrade "github.com/aws/eks-anywhere-build-tooling/projects/aws/upgrader/upgrade" + . "github.com/onsi/gomega" +) + +const ( + kubeadmClusterConfigFileName = "kubeadm-cluster-config.yaml" + kubeletConfigFileName = "kubelet-config.yaml" + staticKubeVipPath = "/etc/kubernetes/manifests/kube-vip.yaml" + kubeAdmBinDir = "/usr/bin/kubeadm" + clusterConfig = ` + apiServer: + extraArgs: + tls-cipher-suites: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + apiVersion: kubeadm.k8s.io/v1beta3 + certificatesDir: /etc/kubernetes/pki + clusterName: dummy-tst + controlPlaneEndpoint: 195.17.1.74:6443 + dns: + imageRepository: public.ecr.aws/eks-distro/coredns + imageTag: v1.9.3-eks-1-25-34 + etcd: + local: + dataDir: /var/lib/etcd + imageRepository: public.ecr.aws/eks-distro/etcd-io + imageTag: v3.5.10-eks-1-28-11 + imageRepository: public.ecr.aws/eks-distro/kubernetes + kind: ClusterConfiguration + kubernetesVersion: v1.29.1-eks-1-29-6 + ` + updatedClusterConfig = ` + apiServer: + extraArgs: + tls-cipher-suites: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + apiVersion: kubeadm.k8s.io/v1beta3 + certificatesDir: /etc/kubernetes/pki + clusterName: dummy-tst + controlPlaneEndpoint: 195.17.1.74:6443 + dns: + imageRepository: public.ecr.aws/eks-distro/coredns + imageTag: v1.9.3-eks-1-25-34 + etcd: + local: + dataDir: /var/lib/etcd + imageRepository: public.ecr.aws/eks-distro/etcd-io + imageTag: v3.5.10-eks-1-29-6 + imageRepository: public.ecr.aws/eks-distro/kubernetes + kind: ClusterConfiguration + kubernetesVersion: v1.29.1-eks-1-29-6 + ` + kubeletConfig = ` + apiVersion: kubelet.config.k8s.io/v1beta1 + cgroupDriver: systemd + clusterDNS: + - 10.96.0.10 + clusterDomain: cluster.local + containerRuntimeEndpoint: "" + kind: KubeletConfiguration + logging: + flushFrequency: 0 + resolvConf: /run/systemd/resolve/resolv.conf + rotateCertificates: true + staticPodPath: /etc/kubernetes/manifests + ` + upgCompBinDir = "/foo/binaries/kubernetes/usr/bin" + kubeVipBackup = "/foo/kube-vip.backup.yaml" + newKubeVip = "/foo/kube-vip.yaml" + coreDNSBackup = "/foo/binaries/kubernetes/usr/bin/coredns.yaml" +) + +func TestKubeAdmFirstCPBackupExist(t *testing.T) { + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + appendedKubeletConfigBytes := []byte(fmt.Sprintf("%s%s%s", clusterConfig, "---\n", kubeletConfig)) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte(updatedClusterConfig), fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, appendedKubeletConfigBytes, fileMode640).Return(nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "plan", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "apply", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig, "--allow-experimental-upgrades", "--yes").Return(nil, nil).Times(1) + tt.s.EXPECT().ReadFile(staticKubeVipPath).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeVipBackup, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeVip).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(staticKubeVipPath, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "create", "-f", coreDNSBackup, "--kubeconfig", kubeConfigPath).Return(nil, nil).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).To(BeNil()) +} + +func TestKubeAdmFirstCPBackupDoesNotExist(t *testing.T) { + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + clusterConfigBytes := []byte(clusterConfig) + appendedKubeletConfigBytes := []byte(fmt.Sprintf("%s%s%s", clusterConfig, "---\n", kubeletConfig)) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte(updatedClusterConfig), fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, appendedKubeletConfigBytes, fileMode640).Return(nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "plan", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "apply", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig, "--allow-experimental-upgrades", "--yes").Return(nil, nil).Times(1) + tt.s.EXPECT().ReadFile(staticKubeVipPath).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeVipBackup, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeVip).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(staticKubeVipPath, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "create", "-f", coreDNSBackup, "--kubeconfig", kubeConfigPath).Return(nil, nil).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).To(BeNil()) +} + +func TestKubeAdmFirstCPBackupError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPGetKCCError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return([]byte{}, errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPKCCBackupError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPKCCBackupReadError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPEtcdUpdateError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPAppendKubeletReadError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(nil, errors.New("")) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPAppendKubeletGetCmdError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(nil, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPAppendKubeletWriteError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + appendedKubeletConfigBytes := []byte(fmt.Sprintf("%s%s%s", clusterConfig, "---\n", kubeletConfig)) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(clusterConfigBytes, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, appendedKubeletConfigBytes, fileMode640).Return(errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPGetCoreDNSCMError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + appendedKubeletConfigBytes := []byte(fmt.Sprintf("%s%s%s", clusterConfig, "---\n", kubeletConfig)) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(clusterConfigBytes, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, appendedKubeletConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPCoreDNSConfWriteError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + coreDNSBackup := fmt.Sprintf("%s/coredns.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + appendedKubeletConfigBytes := []byte(fmt.Sprintf("%s%s%s", clusterConfig, "---\n", kubeletConfig)) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(clusterConfigBytes, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, appendedKubeletConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return([]byte("coredns-conf"), nil).Times(1) + tt.s.EXPECT().WriteFile(coreDNSBackup, []byte("coredns-conf"), fileMode640).Return(errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPCoreDNSCMDeleteError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + coreDNSBackup := fmt.Sprintf("%s/coredns.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + appendedKubeletConfigBytes := []byte(fmt.Sprintf("%s%s%s", clusterConfig, "---\n", kubeletConfig)) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(clusterConfigBytes, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, appendedKubeletConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return([]byte("coredns-conf"), nil).Times(1) + tt.s.EXPECT().WriteFile(coreDNSBackup, []byte("coredns-conf"), fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPKubeAdmVersionCmdError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + coreDNSBackup := fmt.Sprintf("%s/coredns.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + appendedKubeletConfigBytes := []byte(fmt.Sprintf("%s%s%s", clusterConfig, "---\n", kubeletConfig)) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(clusterConfigBytes, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, appendedKubeletConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return([]byte("coredns-conf"), nil).Times(1) + tt.s.EXPECT().WriteFile(coreDNSBackup, []byte("coredns-conf"), fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPKubeAdmUpgPlanError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + coreDNSBackup := fmt.Sprintf("%s/coredns.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + appendedKubeletConfigBytes := []byte(fmt.Sprintf("%s%s%s", clusterConfig, "---\n", kubeletConfig)) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(clusterConfigBytes, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, appendedKubeletConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return([]byte("coredns-conf"), nil).Times(1) + tt.s.EXPECT().WriteFile(coreDNSBackup, []byte("coredns-conf"), fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "plan", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig).Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPKubeAdmUpgApplyError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + coreDNSBackup := fmt.Sprintf("%s/coredns.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + appendedKubeletConfigBytes := []byte(fmt.Sprintf("%s%s%s", clusterConfig, "---\n", kubeletConfig)) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(clusterConfigBytes, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, appendedKubeletConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return([]byte("coredns-conf"), nil).Times(1) + tt.s.EXPECT().WriteFile(coreDNSBackup, []byte("coredns-conf"), fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "plan", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "apply", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig, "--allow-experimental-upgrades", "--yes").Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPKubeVipReadError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + coreDNSBackup := fmt.Sprintf("%s/coredns.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + appendedKubeletConfigBytes := []byte(fmt.Sprintf("%s%s%s", clusterConfig, "---\n", kubeletConfig)) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(clusterConfigBytes, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, appendedKubeletConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return([]byte("coredns-conf"), nil).Times(1) + tt.s.EXPECT().WriteFile(coreDNSBackup, []byte("coredns-conf"), fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "plan", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "apply", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig, "--allow-experimental-upgrades", "--yes").Return(nil, nil).Times(1) + tt.s.EXPECT().ReadFile(staticKubeVipPath).Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPKubeVipBackupError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + coreDNSBackup := fmt.Sprintf("%s/coredns.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + appendedKubeletConfigBytes := []byte(fmt.Sprintf("%s%s%s", clusterConfig, "---\n", kubeletConfig)) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(clusterConfigBytes, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, appendedKubeletConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return([]byte("coredns-conf"), nil).Times(1) + tt.s.EXPECT().WriteFile(coreDNSBackup, []byte("coredns-conf"), fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "plan", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "apply", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig, "--allow-experimental-upgrades", "--yes").Return(nil, nil).Times(1) + tt.s.EXPECT().ReadFile(staticKubeVipPath).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeVipBackup, nil, fileMode640).Return(errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPNewKubeVipReadError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + coreDNSBackup := fmt.Sprintf("%s/coredns.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + appendedKubeletConfigBytes := []byte(fmt.Sprintf("%s%s%s", clusterConfig, "---\n", kubeletConfig)) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(clusterConfigBytes, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, appendedKubeletConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return([]byte("coredns-conf"), nil).Times(1) + tt.s.EXPECT().WriteFile(coreDNSBackup, []byte("coredns-conf"), fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "plan", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "apply", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig, "--allow-experimental-upgrades", "--yes").Return(nil, nil).Times(1) + tt.s.EXPECT().ReadFile(staticKubeVipPath).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeVipBackup, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeVip).Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPNewKubeVipWriteError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + coreDNSBackup := fmt.Sprintf("%s/coredns.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + appendedKubeletConfigBytes := []byte(fmt.Sprintf("%s%s%s", clusterConfig, "---\n", kubeletConfig)) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(clusterConfigBytes, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, appendedKubeletConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return([]byte("coredns-conf"), nil).Times(1) + tt.s.EXPECT().WriteFile(coreDNSBackup, []byte("coredns-conf"), fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "plan", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "apply", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig, "--allow-experimental-upgrades", "--yes").Return(nil, nil).Times(1) + tt.s.EXPECT().ReadFile(staticKubeVipPath).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeVipBackup, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeVip).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(staticKubeVipPath, nil, fileMode640).Return(errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmFirstCPRestoreCoreDNSError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + kubeAdmConfigBackUp := fmt.Sprintf("%s/kubeadm-config.backup.yaml", upgCompBinDir) + newKubeAdmConfig := fmt.Sprintf("%s/kubeadm-config.yaml", upgCompBinDir) + coreDNSBackup := fmt.Sprintf("%s/coredns.yaml", upgCompBinDir) + clusterConfigBytes := []byte(clusterConfig) + appendedKubeletConfigBytes := []byte(fmt.Sprintf("%s%s%s", clusterConfig, "---\n", kubeletConfig)) + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t, upgrade.WithKubernetesVersion("v1.29.1-eks-1-29-6"), upgrade.WithEtcdVersion("v3.5.10-eks-1-29-6")) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubeadm-config", "-ojsonpath='{.data.ClusterConfiguration}'", "--kubeconfig", kubeConfigPath).Return(clusterConfigBytes, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmConfigBackUp, clusterConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeAdmConfigBackUp).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdmConfig).Return(clusterConfigBytes, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "kubelet-config", "-ojsonpath='{.data.kubelet}'", "--kubeconfig", kubeConfigPath).Return([]byte(kubeletConfig), nil).Times(1) + tt.s.EXPECT().WriteFile(newKubeAdmConfig, appendedKubeletConfigBytes, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return([]byte("coredns-conf"), nil).Times(1) + tt.s.EXPECT().WriteFile(coreDNSBackup, []byte("coredns-conf"), fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "plan", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "apply", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration", "--config", newKubeAdmConfig, "--allow-experimental-upgrades", "--yes").Return(nil, nil).Times(1) + tt.s.EXPECT().ReadFile(staticKubeVipPath).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeVipBackup, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeVip).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(staticKubeVipPath, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "create", "-f", coreDNSBackup, "--kubeconfig", kubeConfigPath).Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInFirstCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmRestCPBackupExists(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "node", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration").Return(nil, nil).Times(1) + tt.s.EXPECT().ReadFile(staticKubeVipPath).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeVipBackup, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeVip).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(staticKubeVipPath, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "create", "-f", coreDNSBackup, "--kubeconfig", kubeConfigPath).Return(nil, nil).Times(1) + + err := tt.u.KubeAdmInRestCP(ctx) + tt.Expect(err).To(BeNil()) +} + +func TestKubeAdmRestCPBackupNotExists(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(kubeAdmBackup).Return(nil, errors.New("")) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "node", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration").Return(nil, nil).Times(1) + tt.s.EXPECT().ReadFile(staticKubeVipPath).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeVipBackup, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeVip).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(staticKubeVipPath, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "create", "-f", coreDNSBackup, "--kubeconfig", kubeConfigPath).Return(nil, nil).Times(1) + + err := tt.u.KubeAdmInRestCP(ctx) + tt.Expect(err).To(BeNil()) +} + +func TestKubeAdmRestCPBackupError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(kubeAdmBackup).Return(nil, errors.New("")) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(errors.New("")).Times(1) + + err := tt.u.KubeAdmInRestCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmRestCPBackupCoreDNSError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(kubeAdmBackup).Return(nil, errors.New("")) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInRestCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmRestCPKubeadmVersionCmdError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(kubeAdmBackup).Return(nil, errors.New("")) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInRestCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmRestCPKubeadmUpgradeCmdError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(kubeAdmBackup).Return(nil, errors.New("")) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "node", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration").Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInRestCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmRestCPKubeVipReadError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(kubeAdmBackup).Return(nil, errors.New("")) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "node", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration").Return(nil, nil).Times(1) + tt.s.EXPECT().ReadFile(staticKubeVipPath).Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInRestCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmRestCPKubeVipBackupError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(kubeAdmBackup).Return(nil, errors.New("")) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "node", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration").Return(nil, nil).Times(1) + tt.s.EXPECT().ReadFile(staticKubeVipPath).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeVipBackup, nil, fileMode640).Return(errors.New("")).Times(1) + + err := tt.u.KubeAdmInRestCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmRestCPNewKubeVipReadError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(kubeAdmBackup).Return(nil, errors.New("")) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "node", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration").Return(nil, nil).Times(1) + tt.s.EXPECT().ReadFile(staticKubeVipPath).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeVipBackup, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeVip).Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInRestCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmRestCPNewKubeVipWriteError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(kubeAdmBackup).Return(nil, errors.New("")) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "node", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration").Return(nil, nil).Times(1) + tt.s.EXPECT().ReadFile(staticKubeVipPath).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeVipBackup, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeVip).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(staticKubeVipPath, nil, fileMode640).Return(errors.New("")).Times(1) + + err := tt.u.KubeAdmInRestCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmRestCPRestoreCoreDNSError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(kubeAdmBackup).Return(nil, errors.New("")) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "get", "cm", "-n", "kube-system", "coredns", "-oyaml", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "delete", "cm", "-n", "kube-system", "coredns", "--kubeconfig", kubeConfigPath, "--ignore-not-found=true").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "node", "--ignore-preflight-errors=CoreDNSUnsupportedPlugins,CoreDNSMigration").Return(nil, nil).Times(1) + tt.s.EXPECT().ReadFile(staticKubeVipPath).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeVipBackup, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(newKubeVip).Return(nil, nil).Times(1) + tt.s.EXPECT().WriteFile(staticKubeVipPath, nil, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubectl", "create", "-f", coreDNSBackup, "--kubeconfig", kubeConfigPath).Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInRestCP(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmWorkerBackUpExist(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, nil) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "node").Return(nil, nil).Times(1) + + err := tt.u.KubeAdmInWorker(ctx) + tt.Expect(err).To(BeNil()) +} + +func TestKubeAdmWorkerBackUpNotExist(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "node").Return(nil, nil).Times(1) + + err := tt.u.KubeAdmInWorker(ctx) + tt.Expect(err).To(BeNil()) +} + +func TestKubeAdmWorkerBackUpError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(errors.New("")).Times(1) + + err := tt.u.KubeAdmInWorker(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmWorkerKubeadmVersionError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInWorker(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeAdmWorkerKubeadmUpgradeError(t *testing.T) { + kubeAdmBackup := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm.bk") + newKubeAdm := fmt.Sprintf("%s/%s", upgCompBinDir, "kubeadm") + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).AnyTimes() + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubeadm")).Return(nil, errors.New("")) + tt.s.EXPECT().ReadFile(kubeAdmBinDir).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBackup, []byte{}, fileMode640).Times(1) + tt.s.EXPECT().ReadFile(newKubeAdm).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeAdmBinDir, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "upgrade", "node").Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeAdmInWorker(ctx) + tt.Expect(err).ToNot(BeNil()) +} diff --git a/projects/aws/upgrader/upgrade/kubelet_kubectl.go b/projects/aws/upgrader/upgrade/kubelet_kubectl.go new file mode 100644 index 0000000000..249cd0636c --- /dev/null +++ b/projects/aws/upgrader/upgrade/kubelet_kubectl.go @@ -0,0 +1,110 @@ +package upgrade + +import ( + "bytes" + "context" + "errors" + "fmt" + "io/fs" + "os" + "strings" + + "github.com/aws/eks-anywhere-build-tooling/tools/version-tracker/pkg/util/logger" +) + +const ( + kubectlBinPath = "/usr/bin/kubectl" + kubeletBinPath = "/usr/bin/kubelet" + kubeletConf = "/etc/sysconfig/kubelet" + kubeletCredProviderFeatureGate = " --feature-gates=KubeletCredentialProviders=true" + extraArgs = "extra_args" + fileMode755 = fs.FileMode(0o755) +) + +// KubeletKubectlUpgrade upgrades kubelet and kubectl on the node +// As part of the upgrade: +// 1. backs up kubectl binary and replace it with the new version +// 2. backs up kubelet binary and replace it with the new version +// 3. reload the daemon and restart kubelet with the new version +func (u *InPlaceUpgrader) KubeletKubectlUpgrade(ctx context.Context) error { + cmpDir, err := u.upgradeComponentsKubernetesBinDir() + if err != nil { + return fmt.Errorf("getting upgrade components binary directory: %v", err) + } + + if err := u.BackUpAndReplace(kubectlBinPath, cmpDir, fmt.Sprintf("%s/kubectl", cmpDir)); err != nil { + return fmt.Errorf("backing up and replacing kubectl binary: %v", err) + } + logger.Info("Backed up and replaced kubectl successfully") + + stopKubeletCmd := []string{"systemctl", "stop", "kubelet"} + out, err := u.ExecCommand(ctx, stopKubeletCmd[0], stopKubeletCmd[1:]...) + if err != nil { + return execError(stopKubeletCmd, string(out)) + } + + if err := u.BackUpAndReplace(kubeletBinPath, cmpDir, fmt.Sprintf("%s/kubelet", cmpDir)); err != nil { + return fmt.Errorf("backing up and replacing kubelet binary: %v", err) + } + logger.Info("Backed up and replaced kubelet successfully") + + kubeAdmVersionCmd := []string{"kubeadm", "version", "-oshort"} + kubeVersion, err := u.ExecCommand(ctx, kubeAdmVersionCmd[0], kubeAdmVersionCmd[1:]...) + kubeVersionStr := string(kubeVersion) + if err != nil { + return execError(kubeAdmVersionCmd, kubeVersionStr) + } + + // KubeletCredentialProviders support became GA in k8s v1.26, and the feature gate was removed in k8s v1.28. + // For in-place upgrades, we should remove this feature gate if it exists on nodes running k8s v1.26 and above. + if !strings.Contains(kubeVersionStr, "v1.25") { + if err := u.updateKubeletExtraArgs(cmpDir); err != nil { + return fmt.Errorf("updating kubelet extra args: %v", err) + } + } + + daemonReloadCmd := []string{"systemctl", "daemon-reload"} + out, err = u.ExecCommand(ctx, daemonReloadCmd[0], daemonReloadCmd[1:]...) + if err != nil { + return execError(daemonReloadCmd, string(out)) + } + + restartkubeletCmd := []string{"systemctl", "restart", "kubelet"} + out, err = u.ExecCommand(ctx, restartkubeletCmd[0], restartkubeletCmd[1:]...) + if err != nil { + return execError(restartkubeletCmd, string(out)) + } + + logger.Info("kubectl and kubelet upgrade successful!") + return nil +} + +func (u *InPlaceUpgrader) updateKubeletExtraArgs(cmpDir string) error { + if _, err := u.Stat(kubeletConf); err != nil { + if errors.Is(err, os.ErrNotExist) { + logger.Info("kubelet config file not found, skipping updating extra args", "kubelet_config_path", kubeletConf) + return nil + } + } + + conf, err := u.ReadFile(kubeletConf) + if err != nil { + return fmt.Errorf("reading kubelet config on the node: %v", err) + } + newConf := bytes.ReplaceAll(conf, []byte(kubeletCredProviderFeatureGate), []byte("")) + + extraArgsDir := fmt.Sprintf("%s/%s", cmpDir, extraArgs) + if err = u.MkdirAll(extraArgsDir, fileMode755); err != nil { + return fmt.Errorf("creating folder: %v", err) + } + kubeletConfBackupFile := fmt.Sprintf("%s/kubelet.bk", extraArgsDir) + if err = u.copy(kubeletConf, kubeletConfBackupFile); err != nil { + return copyError(kubeletConf, kubeletConfBackupFile, err) + } + if err := u.WriteFile(kubeletConf, newConf, fileMode640); err != nil { + return fmt.Errorf("writing updated kubelet config to file: %v", err) + } + + logger.Info("removed deprecated feature flags from kubelet config successfully!") + return nil +} diff --git a/projects/aws/upgrader/upgrade/kubelet_kubectl_test.go b/projects/aws/upgrader/upgrade/kubelet_kubectl_test.go new file mode 100644 index 0000000000..7d5c5bf9ff --- /dev/null +++ b/projects/aws/upgrader/upgrade/kubelet_kubectl_test.go @@ -0,0 +1,234 @@ +package upgrade_test + +import ( + "context" + "errors" + "fmt" + "io/fs" + "os" + "testing" + + . "github.com/onsi/gomega" +) + +const ( + kubeletConf = "/etc/sysconfig/kubelet" + kubectlBinPath = "/usr/bin/kubectl" + kubeletBinPath = "/usr/bin/kubelet" + fileMode640 = fs.FileMode(0o640) + fileMode755 = fs.FileMode(0o755) +) + +func Test125KubeletKubectlUpgradeBackupExist(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl")).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "stop", "kubelet").Return(nil, nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubelet")).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version", "-oshort").Return([]byte("v1.25.x"), nil) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "daemon-reload").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "restart", "kubelet").Return(nil, nil).Times(1) + err := tt.u.KubeletKubectlUpgrade(ctx) + tt.Expect(err).To(BeNil()) +} + +func TestNot125KubeletKubectlUpgradeBackupExist(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl")).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "stop", "kubelet").Return(nil, nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubelet")).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version", "-oshort").Return([]byte("v1.26.x"), nil) + tt.s.EXPECT().Stat(kubeletConf).Return(nil, os.ErrNotExist) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "daemon-reload").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "restart", "kubelet").Return(nil, nil).Times(1) + err := tt.u.KubeletKubectlUpgrade(ctx) + tt.Expect(err).To(BeNil()) +} + +func Test125KubeletKubectlUpgradeBackupDoesNotExist(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + kubectlBkFile := fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl") + kubeletBkFile := fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubelet") + + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().Stat(kubectlBkFile).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubectlBinPath).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBkFile, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(fmt.Sprintf("%s/%s", upgCompBinDir, "kubectl")).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBinPath, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "stop", "kubelet").Return(nil, nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubelet")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeletBinPath).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeletBkFile, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(fmt.Sprintf("%s/%s", upgCompBinDir, "kubelet")).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeletBinPath, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version", "-oshort").Return([]byte("v1.25.x"), nil) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "daemon-reload").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "restart", "kubelet").Return(nil, nil).Times(1) + err := tt.u.KubeletKubectlUpgrade(ctx) + tt.Expect(err).To(BeNil()) +} + +func TestNot125KubeletKubectlUpgradeBackupDoesNotExist(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + kubectlBkFile := fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl") + kubeletBkFile := fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubelet") + + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubectlBinPath).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBkFile, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(fmt.Sprintf("%s/%s", upgCompBinDir, "kubectl")).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBinPath, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "stop", "kubelet").Return(nil, nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubelet")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeletBinPath).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeletBkFile, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(fmt.Sprintf("%s/%s", upgCompBinDir, "kubelet")).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeletBinPath, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version", "-oshort").Return([]byte("v1.26.x"), nil) + tt.s.EXPECT().Stat(kubeletConf).Return(nil, os.ErrNotExist) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "daemon-reload").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "restart", "kubelet").Return(nil, nil).Times(1) + tt.u.KubeletKubectlUpgrade(ctx) +} + +func TestNot125KubeletKubectlUpgradeKubeletConfExist(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + kubectlBkFile := fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl") + kubeletBkFile := fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubelet") + oldKubeletConf := []byte("kubelet conf --feature-gates=KubeletCredentialProviders=true") + newKubeletConf := []byte("kubelet conf") + + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubectlBinPath).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBkFile, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(fmt.Sprintf("%s/%s", upgCompBinDir, "kubectl")).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBinPath, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "stop", "kubelet").Return(nil, nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubelet")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeletBinPath).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeletBkFile, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(fmt.Sprintf("%s/%s", upgCompBinDir, "kubelet")).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubeletBinPath, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version", "-oshort").Return([]byte("v1.26.x"), nil) + tt.s.EXPECT().Stat(kubeletConf).Return(nil, nil) + tt.s.EXPECT().ReadFile(kubeletConf).Return(oldKubeletConf, nil) + tt.s.EXPECT().MkdirAll(fmt.Sprintf("%s/%s", upgCompBinDir, "extra_args"), fileMode755).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(kubeletConf).Return(oldKubeletConf, nil) + tt.s.EXPECT().WriteFile(fmt.Sprintf("%s/%s/%s", upgCompBinDir, "extra_args", "kubelet.bk"), oldKubeletConf, fileMode640) + tt.s.EXPECT().WriteFile(kubeletConf, newKubeletConf, fileMode640) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "daemon-reload").Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "restart", "kubelet").Return(nil, nil).Times(1) + tt.u.KubeletKubectlUpgrade(ctx) +} + +func TestKubeletKubectlUpgradeKubectlBackupError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubectlBinPath).Return(nil, errors.New("")).Times(1) + err := tt.u.KubeletKubectlUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeletKubectlUpgradeStopKubeletCmdError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + kubectlBkFile := fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl") + + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubectlBinPath).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBkFile, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(fmt.Sprintf("%s/%s", upgCompBinDir, "kubectl")).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBinPath, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "stop", "kubelet").Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeletKubectlUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeletKubectlUpgradeKubeletBackupError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + kubectlBkFile := fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl") + + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubectlBinPath).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBkFile, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(fmt.Sprintf("%s/%s", upgCompBinDir, "kubectl")).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBinPath, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "stop", "kubelet").Return(nil, nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubelet")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubeletBinPath).Return(nil, errors.New("")).Times(1) + + err := tt.u.KubeletKubectlUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeletKubectlUpgradeKubeadmVersionError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + kubectlBkFile := fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl") + + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubectlBinPath).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBkFile, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(fmt.Sprintf("%s/%s", upgCompBinDir, "kubectl")).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBinPath, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "stop", "kubelet").Return(nil, nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubelet")).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version", "-oshort").Return([]byte("v1.26.x"), errors.New("")) + + err := tt.u.KubeletKubectlUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeletKubectlUpgradeDaemonReloadError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + kubectlBkFile := fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl") + + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubectlBinPath).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBkFile, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(fmt.Sprintf("%s/%s", upgCompBinDir, "kubectl")).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBinPath, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "stop", "kubelet").Return(nil, nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubelet")).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version", "-oshort").Return([]byte("v1.25.x"), nil) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "daemon-reload").Return(nil, errors.New("")).Times(1) + err := tt.u.KubeletKubectlUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} + +func TestKubeletKubectlUpgradeKubeletRestartError(t *testing.T) { + ctx := context.TODO() + tt := newInPlaceUpgraderTest(t) + kubectlBkFile := fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl") + + tt.s.EXPECT().Executable().Return("/foo/eks-upgrades/tools", nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubectl")).Return(nil, errors.New("")).Times(1) + tt.s.EXPECT().ReadFile(kubectlBinPath).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBkFile, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ReadFile(fmt.Sprintf("%s/%s", upgCompBinDir, "kubectl")).Return([]byte{}, nil).Times(1) + tt.s.EXPECT().WriteFile(kubectlBinPath, []byte{}, fileMode640).Return(nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "stop", "kubelet").Return(nil, nil).Times(1) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s.bk", upgCompBinDir, "kubelet")).Return(nil, nil).Times(1) + tt.s.EXPECT().ExecCommand(ctx, "kubeadm", "version", "-oshort").Return([]byte("v1.25.x"), nil) + tt.s.EXPECT().ExecCommand(ctx, "systemctl", "daemon-reload").Return(nil, errors.New("")).Times(1) + err := tt.u.KubeletKubectlUpgrade(ctx) + tt.Expect(err).ToNot(BeNil()) +} diff --git a/projects/aws/upgrader/upgrade/mocks/syscalls.go b/projects/aws/upgrader/upgrade/mocks/syscalls.go new file mode 100644 index 0000000000..e00e8b7733 --- /dev/null +++ b/projects/aws/upgrader/upgrade/mocks/syscalls.go @@ -0,0 +1,19 @@ +//go:generate ../../hack/tools/bin/mockgen -destination ./syscalls_mock.go -package mocks . SysCalls +package mocks + +import ( + context "context" + os "os" +) + +// SysCalls provides a list of system calls for a host. +// Mock implementations can aid in testing without making an underlying os call. +type SysCalls interface { + WriteFile(string, []byte, os.FileMode) error + ReadFile(string) ([]byte, error) + OpenFile(string, int, os.FileMode) (*os.File, error) + Stat(string) (os.FileInfo, error) + Executable() (string, error) + ExecCommand(context.Context, string, ...string) ([]byte, error) + MkdirAll(string, os.FileMode) error +} diff --git a/projects/aws/upgrader/upgrade/mocks/syscalls_mock.go b/projects/aws/upgrader/upgrade/mocks/syscalls_mock.go new file mode 100644 index 0000000000..d0a61a77a5 --- /dev/null +++ b/projects/aws/upgrader/upgrade/mocks/syscalls_mock.go @@ -0,0 +1,145 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/aws/eks-anywhere-build-tooling/projects/aws/upgrader/upgrade/mocks (interfaces: SysCalls) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + fs "io/fs" + os "os" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockSysCalls is a mock of SysCalls interface. +type MockSysCalls struct { + ctrl *gomock.Controller + recorder *MockSysCallsMockRecorder +} + +// MockSysCallsMockRecorder is the mock recorder for MockSysCalls. +type MockSysCallsMockRecorder struct { + mock *MockSysCalls +} + +// NewMockSysCalls creates a new mock instance. +func NewMockSysCalls(ctrl *gomock.Controller) *MockSysCalls { + mock := &MockSysCalls{ctrl: ctrl} + mock.recorder = &MockSysCallsMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSysCalls) EXPECT() *MockSysCallsMockRecorder { + return m.recorder +} + +// ExecCommand mocks base method. +func (m *MockSysCalls) ExecCommand(arg0 context.Context, arg1 string, arg2 ...string) ([]byte, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ExecCommand", varargs...) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExecCommand indicates an expected call of ExecCommand. +func (mr *MockSysCallsMockRecorder) ExecCommand(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecCommand", reflect.TypeOf((*MockSysCalls)(nil).ExecCommand), varargs...) +} + +// Executable mocks base method. +func (m *MockSysCalls) Executable() (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Executable") + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Executable indicates an expected call of Executable. +func (mr *MockSysCallsMockRecorder) Executable() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Executable", reflect.TypeOf((*MockSysCalls)(nil).Executable)) +} + +// MkdirAll mocks base method. +func (m *MockSysCalls) MkdirAll(arg0 string, arg1 fs.FileMode) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MkdirAll", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// MkdirAll indicates an expected call of MkdirAll. +func (mr *MockSysCallsMockRecorder) MkdirAll(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MkdirAll", reflect.TypeOf((*MockSysCalls)(nil).MkdirAll), arg0, arg1) +} + +// OpenFile mocks base method. +func (m *MockSysCalls) OpenFile(arg0 string, arg1 int, arg2 fs.FileMode) (*os.File, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OpenFile", arg0, arg1, arg2) + ret0, _ := ret[0].(*os.File) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// OpenFile indicates an expected call of OpenFile. +func (mr *MockSysCallsMockRecorder) OpenFile(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenFile", reflect.TypeOf((*MockSysCalls)(nil).OpenFile), arg0, arg1, arg2) +} + +// ReadFile mocks base method. +func (m *MockSysCalls) ReadFile(arg0 string) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadFile", arg0) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadFile indicates an expected call of ReadFile. +func (mr *MockSysCallsMockRecorder) ReadFile(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadFile", reflect.TypeOf((*MockSysCalls)(nil).ReadFile), arg0) +} + +// Stat mocks base method. +func (m *MockSysCalls) Stat(arg0 string) (fs.FileInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Stat", arg0) + ret0, _ := ret[0].(fs.FileInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Stat indicates an expected call of Stat. +func (mr *MockSysCallsMockRecorder) Stat(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stat", reflect.TypeOf((*MockSysCalls)(nil).Stat), arg0) +} + +// WriteFile mocks base method. +func (m *MockSysCalls) WriteFile(arg0 string, arg1 []byte, arg2 fs.FileMode) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WriteFile", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// WriteFile indicates an expected call of WriteFile. +func (mr *MockSysCallsMockRecorder) WriteFile(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteFile", reflect.TypeOf((*MockSysCalls)(nil).WriteFile), arg0, arg1, arg2) +} diff --git a/projects/aws/upgrader/upgrade/status.go b/projects/aws/upgrader/upgrade/status.go new file mode 100644 index 0000000000..fe50160e41 --- /dev/null +++ b/projects/aws/upgrader/upgrade/status.go @@ -0,0 +1,55 @@ +package upgrade + +import ( + "context" + "fmt" + + "github.com/aws/eks-anywhere-build-tooling/tools/version-tracker/pkg/util/logger" +) + +// LogStatusAndCleanup logs containerd, kubelet and kubeadm version on the node post upgrade +// Also cleans up the leftover upgrade artifacts after a succesful upgrade +func (u *InPlaceUpgrader) LogStatusAndCleanup(ctx context.Context) error { + if err := u.logStatus(ctx); err != nil { + return fmt.Errorf("logging current status of the node: %v", err) + } + + upgCmpDir, err := u.upgradeComponentsDir() + if err != nil { + return fmt.Errorf("getting upgrade components kubernetes binary directory: %v", err) + } + + cleanUpCmd := []string{"rm", "-rf", upgCmpDir} + out, err := u.ExecCommand(ctx, cleanUpCmd[0], cleanUpCmd[1:]...) + if err != nil { + execError(cleanUpCmd, string(out)) + } + + logger.Info("cleaning up leftover upgrade components", "in-place components directory", upgCmpDir) + return nil +} + +func (u *InPlaceUpgrader) logStatus(ctx context.Context) error { + containerdStatusCmd := []string{"systemctl", "status", "containerd"} + out, err := u.ExecCommand(ctx, containerdStatusCmd[0], containerdStatusCmd[1:]...) + if err != nil { + return execError(containerdStatusCmd, string(out)) + } + logger.Info("containerd status on the node", "status", string(out)) + + kubeletStatusCmd := []string{"systemctl", "status", "kubelet"} + out, err = u.ExecCommand(ctx, kubeletStatusCmd[0], kubeletStatusCmd[1:]...) + if err != nil { + return execError(kubeletStatusCmd, string(out)) + } + logger.Info("kubelet status on the node", "status", string(out)) + + kubeAdmVersionCmd := []string{"kubeadm", "version"} + out, err = u.ExecCommand(ctx, kubeAdmVersionCmd[0], kubeAdmVersionCmd[1:]...) + if err != nil { + execError(kubeAdmVersionCmd, string(out)) + } + logger.Info("kubeadm version on the node", "Version", string(out)) + + return nil +} diff --git a/projects/aws/upgrader/upgrade/syscalls.go b/projects/aws/upgrader/upgrade/syscalls.go new file mode 100644 index 0000000000..9a7f72e044 --- /dev/null +++ b/projects/aws/upgrader/upgrade/syscalls.go @@ -0,0 +1,36 @@ +//go:generate ../hack/tools/bin/mockgen -destination ./mocks/syscalls_mock.go -package mocks . SysCalls + +package upgrade + +import ( + "context" + "os" + "os/exec" +) + +// SysCalls serves different os level calls on a node. +type SysCalls struct { + WriteFile func(string, []byte, os.FileMode) error + ReadFile func(string) ([]byte, error) + OpenFile func(string, int, os.FileMode) (*os.File, error) + Stat func(string) (os.FileInfo, error) + Executable func() (string, error) + ExecCommand func(context.Context, string, ...string) ([]byte, error) + MkdirAll func(string, os.FileMode) error +} + +func ExecCommand(ctx context.Context, name string, arg ...string) ([]byte, error) { + return exec.CommandContext(ctx, name, arg...).CombinedOutput() +} + +func NewSysCalls() SysCalls { + return SysCalls{ + WriteFile: os.WriteFile, + ReadFile: os.ReadFile, + OpenFile: os.OpenFile, + Stat: os.Stat, + Executable: os.Executable, + ExecCommand: ExecCommand, + MkdirAll: os.MkdirAll, + } +} diff --git a/projects/aws/upgrader/upgrade/upgrader.go b/projects/aws/upgrader/upgrade/upgrader.go new file mode 100644 index 0000000000..80c37c91ab --- /dev/null +++ b/projects/aws/upgrader/upgrade/upgrader.go @@ -0,0 +1,123 @@ +package upgrade + +import ( + "fmt" + "io/fs" + "path/filepath" + "strings" + + "github.com/aws/eks-anywhere-build-tooling/tools/version-tracker/pkg/util/logger" +) + +const ( + fileMode640 = fs.FileMode(0o640) +) + +type InPlaceUpgrader struct { + SysCalls + // optional fields + kubernetesVersion string + etcdVersion string +} + +type Option func(u InPlaceUpgrader) InPlaceUpgrader + +func WithKubernetesVersion(version string) Option { + return func(u InPlaceUpgrader) InPlaceUpgrader { + u.kubernetesVersion = version + return u + } +} + +func WithEtcdVersion(version string) Option { + return func(u InPlaceUpgrader) InPlaceUpgrader { + u.etcdVersion = version + return u + } +} + +func (u *InPlaceUpgrader) CurrDir() (string, error) { + ex, err := u.Executable() + if err != nil { + return "", fmt.Errorf("unable to get current directory") + } + + return filepath.Dir(ex), nil +} + +func (u *InPlaceUpgrader) upgradeComponentsDir() (string, error) { + scriptDir, err := u.CurrDir() + if err != nil { + return "", err + } + + return filepath.Dir(scriptDir), nil +} + +func (u *InPlaceUpgrader) upgradeComponentsBinDir() (string, error) { + upgCmpDir, err := u.upgradeComponentsDir() + if err != nil { + return "", err + } + + return fmt.Sprintf("%s/binaries", upgCmpDir), nil +} + +func (u *InPlaceUpgrader) upgradeComponentsKubernetesBinDir() (string, error) { + upgCompBinDir, err := u.upgradeComponentsBinDir() + if err != nil { + return "", err + } + + return fmt.Sprintf("%s/kubernetes/usr/bin", upgCompBinDir), nil +} + +func NewInPlaceUpgrader(options ...Option) InPlaceUpgrader { + upg := InPlaceUpgrader{} + upg.SysCalls = NewSysCalls() + for _, opt := range options { + upg = opt(upg) + } + + return upg +} + +func (u *InPlaceUpgrader) BackUpAndReplace(oldFile, backUpFolder, newFile string) error { + fileName := filepath.Base(oldFile) + backedUpFile := filepath.Join(backUpFolder, fmt.Sprintf("%s.bk", fileName)) + if _, err := u.Stat(backedUpFile); err == nil { + logger.Info("BackUp File already found, skipping backup") + return nil + } + + if err := u.copy(oldFile, backedUpFile); err != nil { + return copyError(oldFile, backedUpFile, err) + } + + if err := u.copy(newFile, oldFile); err != nil { + return copyError(newFile, oldFile, err) + } + logger.Info("BackUp Success", "File", oldFile, "BackedUpFile", backedUpFile) + + return nil +} + +func (u *InPlaceUpgrader) copy(src, dst string) error { + data, err := u.ReadFile(src) + if err != nil { + return err + } + if err := u.WriteFile(dst, data, fileMode640); err != nil { + return err + } + + return nil +} + +func copyError(src, dst string, err error) error { + return fmt.Errorf("copying file from %s to %s: %v", src, dst, err) +} + +func execError(cmd []string, output string) error { + return fmt.Errorf("executing command %s: %s", strings.Join(cmd, " "), output) +} diff --git a/projects/aws/upgrader/upgrade/upgrader_test.go b/projects/aws/upgrader/upgrade/upgrader_test.go new file mode 100644 index 0000000000..3618507468 --- /dev/null +++ b/projects/aws/upgrader/upgrade/upgrader_test.go @@ -0,0 +1,139 @@ +package upgrade_test + +import ( + "context" + "errors" + "fmt" + "io/fs" + "os" + "testing" + + upgrade "github.com/aws/eks-anywhere-build-tooling/projects/aws/upgrader/upgrade" + mock_upgrade "github.com/aws/eks-anywhere-build-tooling/projects/aws/upgrader/upgrade/mocks" + "github.com/golang/mock/gomock" + . "github.com/onsi/gomega" +) + +const ( + kubeConfigPath = "/etc/kubernetes/admin.conf" + testDataDir = "testdata" +) + +type upgraderTest struct { + *WithT + u *upgrade.InPlaceUpgrader + s *mock_upgrade.MockSysCalls +} + +func newSysCallsMock(s *mock_upgrade.MockSysCalls) upgrade.SysCalls { + return upgrade.SysCalls{ + WriteFile: func(name string, data []byte, perm fs.FileMode) error { + return s.WriteFile(name, data, perm) + }, + ReadFile: func(name string) ([]byte, error) { + return s.ReadFile(name) + }, + OpenFile: func(name string, flag int, perm fs.FileMode) (*os.File, error) { + return s.OpenFile(name, flag, perm) + }, + Stat: func(name string) (fs.FileInfo, error) { + return s.Stat(name) + }, + Executable: func() (string, error) { + return s.Executable() + }, + ExecCommand: func(ctx context.Context, name string, arg ...string) ([]byte, error) { + return s.ExecCommand(ctx, name, arg...) + }, + MkdirAll: func(name string, perm fs.FileMode) error { + return s.MkdirAll(name, perm) + }, + } +} + +func newInPlaceUpgraderTest(t *testing.T, options ...upgrade.Option) *upgraderTest { + s := mock_upgrade.NewMockSysCalls(gomock.NewController(t)) + upg := upgrade.NewInPlaceUpgrader(options...) + upg.SysCalls = newSysCallsMock(s) + return &upgraderTest{ + WithT: NewWithT(t), + u: &upg, + s: s, + } +} + +func TestCurrDir(t *testing.T) { + tests := []struct { + testName string + ifErr bool + returnErr string + }{ + { + testName: "exec path found success", + ifErr: false, + returnErr: "", + }, { + testName: "path error", + ifErr: true, + returnErr: "path error", + }, + } + for _, tc := range tests { + t.Run(tc.testName, func(t *testing.T) { + tt := newInPlaceUpgraderTest(t) + if tc.ifErr { + tt.s.EXPECT().Executable().Return("", errors.New(tc.returnErr)) + } else { + tt.s.EXPECT().Executable().Return("", nil) + } + _, err := tt.u.CurrDir() + if tc.ifErr { + tt.Expect(err).NotTo(BeNil()) + } else { + tt.Expect(err).To(BeNil()) + } + }) + } +} + +func TestBackUpAndReplace(t *testing.T) { + tests := []struct { + testName string + ifErr bool + returnErr string + }{ + { + testName: "backup success", + ifErr: false, + returnErr: "", + }, + { + testName: "backup error", + ifErr: true, + returnErr: "backup file path error", + }, + } + for _, tc := range tests { + t.Run(tc.testName, func(t *testing.T) { + oldFile := "/home/dummy/path" + backUpFolder := "home/dummy/backup" + newFile := "path.bk" + tt := newInPlaceUpgraderTest(t) + tt.s.EXPECT().Stat(fmt.Sprintf("%s/%s", backUpFolder, newFile)).Return(nil, errors.New("")) + if tc.ifErr { + tt.s.EXPECT().ReadFile(oldFile).Return(nil, errors.New(tc.returnErr)) + } else { + tt.s.EXPECT().ReadFile(oldFile).Return([]byte{}, nil) + tt.s.EXPECT().WriteFile(fmt.Sprintf("%s/%s", backUpFolder, newFile), []byte{}, fileMode640).Return(nil) + tt.s.EXPECT().ReadFile(newFile).Return([]byte{}, nil) + tt.s.EXPECT().WriteFile(oldFile, []byte{}, fileMode640).Return(nil) + } + err := tt.u.BackUpAndReplace(oldFile, backUpFolder, newFile) + if tc.ifErr { + tt.Expect(err).To(Not(BeNil())) + } else { + tt.Expect(err).To(BeNil()) + } + }) + } +} diff --git a/release/checksums-build.yml b/release/checksums-build.yml index 53a7644e48..ce4525b25e 100644 --- a/release/checksums-build.yml +++ b/release/checksums-build.yml @@ -84,6 +84,12 @@ batch: variables: PROJECT_PATH: projects/aws/rolesanywhere-credential-helper CLONE_URL: https://git-codecommit.us-west-2.amazonaws.com/v1/repos/aws.rolesanywhere-credential-helper + - identifier: aws_upgrader + env: + type: ARM_CONTAINER + compute-type: BUILD_GENERAL1_LARGE + variables: + PROJECT_PATH: projects/aws/upgrader - identifier: aws_containers_hello_eks_anywhere env: type: ARM_CONTAINER @@ -515,6 +521,7 @@ batch: - aws_etcdadm_controller - aws_image_builder - aws_rolesanywhere_credential_helper + - aws_upgrader - aws_containers_hello_eks_anywhere - aws_observability_aws_otel_collector - brancz_kube_rbac_proxy