mirror of
https://github.com/wahyd4/cert-manager.git
synced 2026-08-09 05:06:38 +10:00
add ACME HTTP01 Istio support
Signed-off-by: Inteon <42113979+inteon@users.noreply.github.com>
This commit is contained in:
@@ -15993,6 +15993,426 @@ limitations under the License.
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/istio.io/api licensed under: =
|
||||
|
||||
|
||||
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 2016-2020 Istio Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
= vendor/istio.io/api/LICENSE c4d266576c16d0427812c22cafa149c1
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/istio.io/gogo-genproto licensed under: =
|
||||
|
||||
|
||||
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 2016-2019 Istio Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
= vendor/istio.io/gogo-genproto/LICENSE 7f1c1dda4011e02cc15ed3f4777e1358
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/k8s.io/api licensed under: =
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ go_library(
|
||||
"//pkg/controller/issuers:go_default_library",
|
||||
"//pkg/issuer/acme:go_default_library",
|
||||
"//pkg/issuer/acme/dns/util:go_default_library",
|
||||
"//pkg/issuer/acme/http/internal/istio:go_default_library",
|
||||
"//pkg/issuer/ca:go_default_library",
|
||||
"//pkg/issuer/selfsigned:go_default_library",
|
||||
"//pkg/issuer/vault:go_default_library",
|
||||
@@ -33,9 +34,12 @@ go_library(
|
||||
"//pkg/util/feature:go_default_library",
|
||||
"@com_github_spf13_cobra//:go_default_library",
|
||||
"@io_k8s_api//core/v1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/api/errors:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/api/resource:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/util/errors:go_default_library",
|
||||
"@io_k8s_client_go//dynamic:go_default_library",
|
||||
"@io_k8s_client_go//dynamic/dynamicinformer:go_default_library",
|
||||
"@io_k8s_client_go//informers:go_default_library",
|
||||
"@io_k8s_client_go//kubernetes:go_default_library",
|
||||
"@io_k8s_client_go//kubernetes/scheme:go_default_library",
|
||||
|
||||
@@ -24,8 +24,11 @@ import (
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
dynamicclient "k8s.io/client-go/dynamic"
|
||||
dynamicinformers "k8s.io/client-go/dynamic/dynamicinformer"
|
||||
kubeinformers "k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
@@ -45,6 +48,7 @@ import (
|
||||
"github.com/jetstack/cert-manager/pkg/controller"
|
||||
"github.com/jetstack/cert-manager/pkg/controller/clusterissuers"
|
||||
dnsutil "github.com/jetstack/cert-manager/pkg/issuer/acme/dns/util"
|
||||
"github.com/jetstack/cert-manager/pkg/issuer/acme/http/internal/istio"
|
||||
logf "github.com/jetstack/cert-manager/pkg/logs"
|
||||
"github.com/jetstack/cert-manager/pkg/metrics"
|
||||
"github.com/jetstack/cert-manager/pkg/util"
|
||||
@@ -68,6 +72,26 @@ func Run(opts *options.ControllerOptions, stopCh <-chan struct{}) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ctx.IstioEnabled, err = isIstioInstalled(ctx)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to discover if Istio is available")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if ctx.IstioEnabled {
|
||||
ctx.IstioEnabled, err = canListVirtualService(rootCtx, ctx, opts.Namespace)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to list Istio VirtualServices")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.IstioEnabled {
|
||||
log.Info("Istio support is enabled")
|
||||
} else {
|
||||
log.Info("Istio support is disabled")
|
||||
}
|
||||
|
||||
enabledControllers := opts.EnabledControllers()
|
||||
log.Info(fmt.Sprintf("enabled controllers: %s", enabledControllers.List()))
|
||||
|
||||
@@ -117,6 +141,9 @@ func Run(opts *options.ControllerOptions, stopCh <-chan struct{}) {
|
||||
log.V(logf.DebugLevel).Info("starting shared informer factories")
|
||||
ctx.SharedInformerFactory.Start(stopCh)
|
||||
ctx.KubeSharedInformerFactory.Start(stopCh)
|
||||
if ctx.IstioEnabled {
|
||||
ctx.DynamicSharedInformerFactory.Start(stopCh)
|
||||
}
|
||||
wg.Wait()
|
||||
log.V(logf.InfoLevel).Info("control loops exited")
|
||||
ctx.Metrics.Shutdown(metricsServer)
|
||||
@@ -138,6 +165,33 @@ func Run(opts *options.ControllerOptions, stopCh <-chan struct{}) {
|
||||
startLeaderElection(rootCtx, opts, leaderElectionClient, ctx.Recorder, run)
|
||||
}
|
||||
|
||||
func isIstioInstalled(ctx *controller.Context) (bool, error) {
|
||||
groups, err := ctx.Client.Discovery().ServerGroups()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, group := range groups.Groups {
|
||||
if group.Name == istio.VirtualServiceGvr().Group {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func canListVirtualService(rootCtx context.Context, ctx *controller.Context, namespace string) (bool, error) {
|
||||
// Check if sa has permissions to list virtualservice
|
||||
_, err := ctx.DynamicClient.Resource(istio.VirtualServiceGvr()).Namespace(namespace).List(rootCtx, metav1.ListOptions{})
|
||||
if errors.IsForbidden(err) {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func buildControllerContext(ctx context.Context, stopCh <-chan struct{}, opts *options.ControllerOptions) (*controller.Context, *rest.Config, error) {
|
||||
log := logf.FromContext(ctx, "build-context")
|
||||
// Load the users Kubernetes config
|
||||
@@ -164,6 +218,11 @@ func buildControllerContext(ctx context.Context, stopCh <-chan struct{}, opts *o
|
||||
return nil, nil, fmt.Errorf("error creating kubernetes client: %s", err.Error())
|
||||
}
|
||||
|
||||
dyncl, err := dynamicclient.NewForConfig(kubeCfg)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error creating dynamic client: %s", err.Error())
|
||||
}
|
||||
|
||||
nameservers := opts.DNS01RecursiveNameservers
|
||||
if len(nameservers) == 0 {
|
||||
nameservers = dnsutil.RecursiveNameservers
|
||||
@@ -202,21 +261,24 @@ func buildControllerContext(ctx context.Context, stopCh <-chan struct{}, opts *o
|
||||
|
||||
sharedInformerFactory := informers.NewSharedInformerFactoryWithOptions(intcl, resyncPeriod, informers.WithNamespace(opts.Namespace))
|
||||
kubeSharedInformerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(cl, resyncPeriod, kubeinformers.WithNamespace(opts.Namespace))
|
||||
dynamicSharedInformerFactory := dynamicinformers.NewFilteredDynamicSharedInformerFactory(dyncl, resyncPeriod, opts.Namespace, nil)
|
||||
|
||||
acmeAccountRegistry := accounts.NewDefaultRegistry()
|
||||
|
||||
return &controller.Context{
|
||||
RootContext: ctx,
|
||||
StopCh: stopCh,
|
||||
RESTConfig: kubeCfg,
|
||||
Client: cl,
|
||||
CMClient: intcl,
|
||||
Recorder: recorder,
|
||||
KubeSharedInformerFactory: kubeSharedInformerFactory,
|
||||
SharedInformerFactory: sharedInformerFactory,
|
||||
Namespace: opts.Namespace,
|
||||
Clock: clock.RealClock{},
|
||||
Metrics: metrics.New(log),
|
||||
RootContext: ctx,
|
||||
StopCh: stopCh,
|
||||
RESTConfig: kubeCfg,
|
||||
Client: cl,
|
||||
DynamicClient: dyncl,
|
||||
CMClient: intcl,
|
||||
Recorder: recorder,
|
||||
KubeSharedInformerFactory: kubeSharedInformerFactory,
|
||||
DynamicSharedInformerFactory: dynamicSharedInformerFactory,
|
||||
SharedInformerFactory: sharedInformerFactory,
|
||||
Namespace: opts.Namespace,
|
||||
Clock: clock.RealClock{},
|
||||
Metrics: metrics.New(log),
|
||||
ACMEOptions: controller.ACMEOptions{
|
||||
HTTP01SolverImage: opts.ACMEHTTP01SolverImage,
|
||||
HTTP01SolverResourceRequestCPU: HTTP01SolverResourceRequestCPU,
|
||||
|
||||
@@ -223,6 +223,12 @@ rules:
|
||||
- apiGroups: ["networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get", "list", "watch", "create", "delete", "update"]
|
||||
# HTTP01 Istio rules
|
||||
# Need to be able to create and update Istio virtualservices that are used
|
||||
# to route HTTP01 requests to the challenge solver pod
|
||||
- apiGroups: ["networking.istio.io"]
|
||||
resources: ["virtualservices"]
|
||||
verbs: ["get", "list", "watch", "create", "delete", "update"]
|
||||
# We require the ability to specify a custom hostname when we are creating
|
||||
# new ingress resources.
|
||||
# See: https://github.com/openshift/origin/blob/21f191775636f9acadb44fa42beeb4f75b255532/pkg/route/apiserver/admission/ingress_admission.go#L84-L148
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -37,6 +37,8 @@ data:
|
||||
@ IN NS localhost.
|
||||
*.ingress-nginx IN A {SERVICE_IP_PREFIX}.15
|
||||
ingress-nginx IN A {SERVICE_IP_PREFIX}.15
|
||||
*.istio IN A {SERVICE_IP_PREFIX}.14
|
||||
istio IN A {SERVICE_IP_PREFIX}.14
|
||||
|
||||
db.dns01.example.com: |
|
||||
;
|
||||
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2021 The cert-manager Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -o nounset
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
||||
# Installs Istio using istioctl and the manifests located in manifests/
|
||||
# Configure the cluster to target using the KUBECONFIG environment variable.
|
||||
# Additional parameters can be configured by overriding the variables below.
|
||||
|
||||
SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")
|
||||
source "${SCRIPT_ROOT}/../../lib/lib.sh"
|
||||
SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")
|
||||
|
||||
check_tool kubectl
|
||||
check_tool istioctl
|
||||
|
||||
# Namespace to deploy into
|
||||
NAMESPACE="${NAMESPACE:-istio-system}"
|
||||
|
||||
SERVICE_IP_PREFIX="${SERVICE_IP_PREFIX:-10.0.0}"
|
||||
|
||||
istioctl install \
|
||||
--skip-confirmation \
|
||||
--set profile=default \
|
||||
--set values.global.jwtPolicy=first-party-jwt \
|
||||
--set components.ingressGateways[0].name="istio-ingressgateway" \
|
||||
--set components.ingressGateways[0].k8s.service.type="ClusterIP" \
|
||||
--set components.ingressGateways[0].k8s.service.clusterIP="${SERVICE_IP_PREFIX}.14"
|
||||
|
||||
kubectl --namespace "${NAMESPACE}" apply -f "${SCRIPT_ROOT}/manifests/gateway.yaml"
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: networking.istio.io/v1beta1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: ingress
|
||||
spec:
|
||||
selector:
|
||||
istio: ingressgateway
|
||||
servers:
|
||||
- hosts:
|
||||
- '*'
|
||||
port:
|
||||
name: http
|
||||
number: 80
|
||||
protocol: HTTP
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2021 The cert-manager Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -o nounset
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
||||
if ! command -v bazel &>/dev/null; then
|
||||
echo "Install bazel at https://bazel.build" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${ISTIOCTL:-}" ]; then
|
||||
bazel build //hack/bin:istioctl
|
||||
export ISTIOCTL="$(bazel info bazel-genfiles)/hack/bin/istioctl"
|
||||
fi
|
||||
|
||||
"${ISTIOCTL}" "$@"
|
||||
+3
-1
@@ -34,12 +34,13 @@ export OPENSHIFT_VERSION="${OPENSHIFT_VERSION:-"3.11"}"
|
||||
export SERVICE_IP_PREFIX="${SERVICE_IP_PREFIX:-10.0.0}"
|
||||
export DNS_SERVER="${SERVICE_IP_PREFIX}.16"
|
||||
export INGRESS_IP="${SERVICE_IP_PREFIX}.15"
|
||||
export ISTIO_IP="${SERVICE_IP_PREFIX}.14"
|
||||
|
||||
# setup_tools will build and set up the environment to use bazel-provided
|
||||
# versions of the tools required for development
|
||||
setup_tools() {
|
||||
check_bazel
|
||||
bazel build //hack/bin:helm //hack/bin:kind //hack/bin:kubectl //hack/bin:kustomize //devel/bin:ginkgo
|
||||
bazel build //hack/bin:helm //hack/bin:kind //hack/bin:kubectl //hack/bin:kustomize //devel/bin:ginkgo //hack/bin:istioctl
|
||||
if [[ "$IS_OPENSHIFT" == "true" ]] ; then
|
||||
bazel build //hack/bin:oc3
|
||||
fi
|
||||
@@ -48,6 +49,7 @@ setup_tools() {
|
||||
export KIND="${bindir}/hack/bin/kind"
|
||||
export OC3="${bindir}/hack/bin/oc3"
|
||||
export KUBECTL="${bindir}/hack/bin/kubectl"
|
||||
export ISTIOCTL="${bindir}/hack/bin/istioctl"
|
||||
export KUSTOMIZE="${bindir}/hack/bin/kustomize"
|
||||
export GINKGO="${bindir}/devel/bin/ginkgo"
|
||||
# Configure PATH to use bazel provided e2e tools
|
||||
|
||||
@@ -46,4 +46,5 @@ ginkgo -nodes 10 -flakeAttempts ${FLAKE_ATTEMPTS:-1} \
|
||||
--report-dir="${ARTIFACTS:-$REPO_ROOT/_artifacts}" \
|
||||
--acme-dns-server="$DNS_SERVER" \
|
||||
--acme-ingress-ip="$INGRESS_IP" \
|
||||
--acme-istio-ip="$ISTIO_IP" \
|
||||
"$@"
|
||||
|
||||
@@ -31,6 +31,9 @@ source "${SCRIPT_ROOT}/lib/lib.sh"
|
||||
# Configure PATH to use bazel provided e2e tools
|
||||
setup_tools
|
||||
|
||||
echo "Installing istio into the kind cluster..."
|
||||
"${SCRIPT_ROOT}/addon/istio/install.sh"
|
||||
|
||||
echo "Installing cert-manager into the cluster..."
|
||||
"${SCRIPT_ROOT}/addon/certmanager/install.sh"
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ require (
|
||||
gopkg.in/ini.v1 v1.52.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c // indirect
|
||||
istio.io/api v0.0.0-20210114003959-328c3a371318
|
||||
k8s.io/api v0.19.0
|
||||
k8s.io/apiextensions-apiserver v0.19.0
|
||||
k8s.io/apimachinery v0.19.0
|
||||
|
||||
@@ -124,6 +124,8 @@ github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJ
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cloudflare-go v0.13.2 h1:bhMGoNhAg21DuqJjU9jQepRRft6vYfo6pejT3NN4V6A=
|
||||
github.com/cloudflare/cloudflare-go v0.13.2/go.mod h1:27kfc1apuifUmJhp069y0+hwlKDg4bd8LWlu7oKeZvM=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
|
||||
@@ -184,8 +186,10 @@ github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkg
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473 h1:4cmBvAEBNJaGARUEs3/suWRyfyBfhf7I60WBZq+bv2w=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
@@ -291,6 +295,7 @@ github.com/gobuffalo/flect v0.2.0 h1:EWCvMGGxOjsgwlWaP+f4+Hh6yrrte7JeFL2S6b+0hdM
|
||||
github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
@@ -939,9 +944,11 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
|
||||
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.1 h1:C1QC6KzgSiLyBabDi87BbjaGreoRgGUF5nOyvfrAZ1k=
|
||||
google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -1004,6 +1011,10 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
istio.io/api v0.0.0-20210114003959-328c3a371318 h1:U0KZYCOKILJw51SWV5vpcR4FMTXl1CmA70aFy4zMKrU=
|
||||
istio.io/api v0.0.0-20210114003959-328c3a371318/go.mod h1:88HN3o1fSD1jo+Z1WTLlJfMm9biopur6Ct9BFKjiB64=
|
||||
istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a h1:w7zILua2dnYo9CxImhpNW4NE/8ZxEoc/wfBfHrhUhrE=
|
||||
istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a/go.mod h1:OzpAts7jljZceG4Vqi5/zXy/pOg1b209T3jb7Nv5wIs=
|
||||
k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8=
|
||||
k8s.io/api v0.18.6/go.mod h1:eeyxr+cwCjMdLAmr2W3RyDI0VvTawSg/3RFFBEnmZGI=
|
||||
k8s.io/api v0.19.0 h1:XyrFIJqTYZJ2DU7FBE/bSPz7b1HvbVBuBf07oeo6eTc=
|
||||
@@ -1013,6 +1024,7 @@ k8s.io/apiextensions-apiserver v0.18.6/go.mod h1:lv89S7fUysXjLZO7ke783xOwVTm6lKi
|
||||
k8s.io/apiextensions-apiserver v0.19.0 h1:jlY13lvZp+0p9fRX2khHFdiT9PYzT7zUrANz6R1NKtY=
|
||||
k8s.io/apiextensions-apiserver v0.19.0/go.mod h1:znfQxNpjqz/ZehvbfMg5N6fvBJW5Lqu5HVLTJQdP4Fs=
|
||||
k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
|
||||
k8s.io/apimachinery v0.18.1/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
|
||||
k8s.io/apimachinery v0.18.6/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
|
||||
k8s.io/apimachinery v0.19.0 h1:gjKnAda/HZp5k4xQYjL0K/Yb66IvNqjthCb03QlKpaQ=
|
||||
k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
|
||||
|
||||
@@ -80,6 +80,18 @@ genrule(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "fetch_istioctl",
|
||||
srcs = select({
|
||||
":darwin": ["@istioctl_darwin//:file"],
|
||||
":k8": ["@istioctl_linux//:file"],
|
||||
}),
|
||||
outs = ["istioctl"],
|
||||
cmd = "cp $(SRCS) $@",
|
||||
tags = ["manual"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "fetch_oc3",
|
||||
srcs = select({
|
||||
|
||||
@@ -23,6 +23,7 @@ def install():
|
||||
install_staticcheck()
|
||||
install_helm()
|
||||
install_kubectl()
|
||||
install_istioctl()
|
||||
install_oc3()
|
||||
install_kind()
|
||||
install_kustomize()
|
||||
@@ -232,6 +233,38 @@ def install_kubectl():
|
||||
urls = ["https://storage.googleapis.com/kubernetes-release/release/v1.18.0/bin/linux/amd64/kubectl"],
|
||||
)
|
||||
|
||||
def install_istioctl():
|
||||
http_archive(
|
||||
name = "istioctl_darwin",
|
||||
sha256 = "fa0cca95e5c4b1d1084946a8d7953d4a1e830d80c36ef10bbbfae8ce4480ccc8",
|
||||
urls = ["https://github.com/istio/istio/releases/download/1.9.3/istioctl-1.9.3-osx.tar.gz"],
|
||||
build_file_content =
|
||||
"""
|
||||
filegroup(
|
||||
name = "file",
|
||||
srcs = [
|
||||
"istioctl",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "istioctl_linux",
|
||||
sha256 = "f820aa0e0e85a5c5a5b20c1409f03e58d9783646b5976db2ef85ddac12b43848",
|
||||
urls = ["https://github.com/istio/istio/releases/download/1.9.3/istioctl-1.9.3-linux-amd64.tar.gz"],
|
||||
build_file_content =
|
||||
"""
|
||||
filegroup(
|
||||
name = "file",
|
||||
srcs = [
|
||||
"istioctl",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
)
|
||||
|
||||
# Define rules for different oc versions
|
||||
def install_oc3():
|
||||
|
||||
+21
-4
@@ -719,8 +719,8 @@ def go_repositories():
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable",
|
||||
importpath = "github.com/envoyproxy/go-control-plane",
|
||||
sum = "h1:4cmBvAEBNJaGARUEs3/suWRyfyBfhf7I60WBZq+bv2w=",
|
||||
version = "v0.9.1-0.20191026205805-5f8ba28d4473",
|
||||
sum = "h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E=",
|
||||
version = "v0.9.4",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_envoyproxy_protoc_gen_validate",
|
||||
@@ -3063,6 +3063,23 @@ def go_repositories():
|
||||
sum = "h1:Gqga3zA9tdAcfqobUGjSoCob5L3f8Dt5EuOp3ihNZko=",
|
||||
version = "v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5",
|
||||
)
|
||||
go_repository(
|
||||
name = "io_istio_api",
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable",
|
||||
importpath = "istio.io/api",
|
||||
sum = "h1:U0KZYCOKILJw51SWV5vpcR4FMTXl1CmA70aFy4zMKrU=",
|
||||
version = "v0.0.0-20210114003959-328c3a371318",
|
||||
)
|
||||
go_repository(
|
||||
name = "io_istio_gogo_genproto",
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable",
|
||||
importpath = "istio.io/gogo-genproto",
|
||||
sum = "h1:w7zILua2dnYo9CxImhpNW4NE/8ZxEoc/wfBfHrhUhrE=",
|
||||
version = "v0.0.0-20190930162913-45029607206a",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "io_k8s_api",
|
||||
build_file_generation = "on",
|
||||
@@ -3356,8 +3373,8 @@ def go_repositories():
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable",
|
||||
importpath = "google.golang.org/grpc",
|
||||
sum = "h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=",
|
||||
version = "v1.27.0",
|
||||
sum = "h1:C1QC6KzgSiLyBabDi87BbjaGreoRgGUF5nOyvfrAZ1k=",
|
||||
version = "v1.28.1",
|
||||
)
|
||||
go_repository(
|
||||
name = "org_golang_google_grpc_examples",
|
||||
|
||||
@@ -204,6 +204,14 @@ type ACMEChallengeSolverHTTP01 struct {
|
||||
// provisioned by cert-manager for each Challenge to be completed.
|
||||
// +optional
|
||||
Ingress *ACMEChallengeSolverHTTP01Ingress `json:"ingress,omitempty"`
|
||||
|
||||
// The Istio virtualservice based HTTP01 challenge solver will solve
|
||||
// challenges by creating an Istio virtualservice resource that is connected
|
||||
// to the specified Istio gateway in order to route requests for
|
||||
// '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are
|
||||
// provisioned by cert-manager for each Challenge to be completed.
|
||||
// +optional
|
||||
Istio *ACMEChallengeSolverHTTP01Istio `json:"istio,omitempty"`
|
||||
}
|
||||
|
||||
type ACMEChallengeSolverHTTP01Ingress struct {
|
||||
@@ -305,6 +313,19 @@ type ACMEChallengeSolverHTTP01IngressObjectMeta struct {
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
type ACMEChallengeSolverHTTP01Istio struct {
|
||||
// Namespace of the gateway that is used to generate the virtualservice for.
|
||||
GatewayNamespace string `json:"gatewayNamespace"`
|
||||
|
||||
// Name of the gateway that is used to generate the virtualservice for.
|
||||
GatewayName string `json:"gatewayName"`
|
||||
|
||||
// Optional pod template used to configure the ACME challenge solver pods
|
||||
// used for HTTP01 challenges
|
||||
// +optional
|
||||
PodTemplate *ACMEChallengeSolverHTTP01IngressPodTemplate `json:"podTemplate,omitempty"`
|
||||
}
|
||||
|
||||
// Used to configure a DNS01 challenge provider to be used when solving DNS01
|
||||
// challenges.
|
||||
// Only one DNS provider may be configured per solver.
|
||||
|
||||
@@ -170,6 +170,11 @@ func (in *ACMEChallengeSolverHTTP01) DeepCopyInto(out *ACMEChallengeSolverHTTP01
|
||||
*out = new(ACMEChallengeSolverHTTP01Ingress)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Istio != nil {
|
||||
in, out := &in.Istio, &out.Istio
|
||||
*out = new(ACMEChallengeSolverHTTP01Istio)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -344,6 +349,27 @@ func (in *ACMEChallengeSolverHTTP01IngressTemplate) DeepCopy() *ACMEChallengeSol
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ACMEChallengeSolverHTTP01Istio) DeepCopyInto(out *ACMEChallengeSolverHTTP01Istio) {
|
||||
*out = *in
|
||||
if in.PodTemplate != nil {
|
||||
in, out := &in.PodTemplate, &out.PodTemplate
|
||||
*out = new(ACMEChallengeSolverHTTP01IngressPodTemplate)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01Istio.
|
||||
func (in *ACMEChallengeSolverHTTP01Istio) DeepCopy() *ACMEChallengeSolverHTTP01Istio {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ACMEChallengeSolverHTTP01Istio)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ACMEExternalAccountBinding) DeepCopyInto(out *ACMEExternalAccountBinding) {
|
||||
*out = *in
|
||||
|
||||
@@ -204,6 +204,14 @@ type ACMEChallengeSolverHTTP01 struct {
|
||||
// provisioned by cert-manager for each Challenge to be completed.
|
||||
// +optional
|
||||
Ingress *ACMEChallengeSolverHTTP01Ingress `json:"ingress,omitempty"`
|
||||
|
||||
// The Istio virtualservice based HTTP01 challenge solver will solve
|
||||
// challenges by creating an Istio virtualservice resource that is connected
|
||||
// to the specified Istio gateway in order to route requests for
|
||||
// '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are
|
||||
// provisioned by cert-manager for each Challenge to be completed.
|
||||
// +optional
|
||||
Istio *ACMEChallengeSolverHTTP01Istio `json:"istio,omitempty"`
|
||||
}
|
||||
|
||||
type ACMEChallengeSolverHTTP01Ingress struct {
|
||||
@@ -305,6 +313,19 @@ type ACMEChallengeSolverHTTP01IngressObjectMeta struct {
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
type ACMEChallengeSolverHTTP01Istio struct {
|
||||
// Namespace of the gateway that is used to generate the virtualservice for.
|
||||
GatewayNamespace string `json:"gatewayNamespace"`
|
||||
|
||||
// Name of the gateway that is used to generate the virtualservice for.
|
||||
GatewayName string `json:"gatewayName"`
|
||||
|
||||
// Optional pod template used to configure the ACME challenge solver pods
|
||||
// used for HTTP01 challenges
|
||||
// +optional
|
||||
PodTemplate *ACMEChallengeSolverHTTP01IngressPodTemplate `json:"podTemplate,omitempty"`
|
||||
}
|
||||
|
||||
// Used to configure a DNS01 challenge provider to be used when solving DNS01
|
||||
// challenges.
|
||||
// Only one DNS provider may be configured per solver.
|
||||
|
||||
@@ -170,6 +170,11 @@ func (in *ACMEChallengeSolverHTTP01) DeepCopyInto(out *ACMEChallengeSolverHTTP01
|
||||
*out = new(ACMEChallengeSolverHTTP01Ingress)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Istio != nil {
|
||||
in, out := &in.Istio, &out.Istio
|
||||
*out = new(ACMEChallengeSolverHTTP01Istio)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -344,6 +349,27 @@ func (in *ACMEChallengeSolverHTTP01IngressTemplate) DeepCopy() *ACMEChallengeSol
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ACMEChallengeSolverHTTP01Istio) DeepCopyInto(out *ACMEChallengeSolverHTTP01Istio) {
|
||||
*out = *in
|
||||
if in.PodTemplate != nil {
|
||||
in, out := &in.PodTemplate, &out.PodTemplate
|
||||
*out = new(ACMEChallengeSolverHTTP01IngressPodTemplate)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01Istio.
|
||||
func (in *ACMEChallengeSolverHTTP01Istio) DeepCopy() *ACMEChallengeSolverHTTP01Istio {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ACMEChallengeSolverHTTP01Istio)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ACMEExternalAccountBinding) DeepCopyInto(out *ACMEExternalAccountBinding) {
|
||||
*out = *in
|
||||
|
||||
@@ -204,6 +204,14 @@ type ACMEChallengeSolverHTTP01 struct {
|
||||
// provisioned by cert-manager for each Challenge to be completed.
|
||||
// +optional
|
||||
Ingress *ACMEChallengeSolverHTTP01Ingress `json:"ingress,omitempty"`
|
||||
|
||||
// The Istio virtualservice based HTTP01 challenge solver will solve
|
||||
// challenges by creating an Istio virtualservice resource that is connected
|
||||
// to the specified Istio gateway in order to route requests for
|
||||
// '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are
|
||||
// provisioned by cert-manager for each Challenge to be completed.
|
||||
// +optional
|
||||
Istio *ACMEChallengeSolverHTTP01Istio `json:"istio,omitempty"`
|
||||
}
|
||||
|
||||
type ACMEChallengeSolverHTTP01Ingress struct {
|
||||
@@ -305,6 +313,19 @@ type ACMEChallengeSolverHTTP01IngressObjectMeta struct {
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
type ACMEChallengeSolverHTTP01Istio struct {
|
||||
// Namespace of the gateway that is used to generate the virtualservice for.
|
||||
GatewayNamespace string `json:"gatewayNamespace"`
|
||||
|
||||
// Name of the gateway that is used to generate the virtualservice for.
|
||||
GatewayName string `json:"gatewayName"`
|
||||
|
||||
// Optional pod template used to configure the ACME challenge solver pods
|
||||
// used for HTTP01 challenges
|
||||
// +optional
|
||||
PodTemplate *ACMEChallengeSolverHTTP01IngressPodTemplate `json:"podTemplate,omitempty"`
|
||||
}
|
||||
|
||||
// Used to configure a DNS01 challenge provider to be used when solving DNS01
|
||||
// challenges.
|
||||
// Only one DNS provider may be configured per solver.
|
||||
|
||||
@@ -170,6 +170,11 @@ func (in *ACMEChallengeSolverHTTP01) DeepCopyInto(out *ACMEChallengeSolverHTTP01
|
||||
*out = new(ACMEChallengeSolverHTTP01Ingress)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Istio != nil {
|
||||
in, out := &in.Istio, &out.Istio
|
||||
*out = new(ACMEChallengeSolverHTTP01Istio)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -344,6 +349,27 @@ func (in *ACMEChallengeSolverHTTP01IngressTemplate) DeepCopy() *ACMEChallengeSol
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ACMEChallengeSolverHTTP01Istio) DeepCopyInto(out *ACMEChallengeSolverHTTP01Istio) {
|
||||
*out = *in
|
||||
if in.PodTemplate != nil {
|
||||
in, out := &in.PodTemplate, &out.PodTemplate
|
||||
*out = new(ACMEChallengeSolverHTTP01IngressPodTemplate)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01Istio.
|
||||
func (in *ACMEChallengeSolverHTTP01Istio) DeepCopy() *ACMEChallengeSolverHTTP01Istio {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ACMEChallengeSolverHTTP01Istio)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ACMEExternalAccountBinding) DeepCopyInto(out *ACMEExternalAccountBinding) {
|
||||
*out = *in
|
||||
|
||||
@@ -204,6 +204,14 @@ type ACMEChallengeSolverHTTP01 struct {
|
||||
// provisioned by cert-manager for each Challenge to be completed.
|
||||
// +optional
|
||||
Ingress *ACMEChallengeSolverHTTP01Ingress `json:"ingress,omitempty"`
|
||||
|
||||
// The Istio virtualservice based HTTP01 challenge solver will solve
|
||||
// challenges by creating an Istio virtualservice resource that is connected
|
||||
// to the specified Istio gateway in order to route requests for
|
||||
// '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are
|
||||
// provisioned by cert-manager for each Challenge to be completed.
|
||||
// +optional
|
||||
Istio *ACMEChallengeSolverHTTP01Istio `json:"istio,omitempty"`
|
||||
}
|
||||
|
||||
type ACMEChallengeSolverHTTP01Ingress struct {
|
||||
@@ -305,6 +313,19 @@ type ACMEChallengeSolverHTTP01IngressObjectMeta struct {
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
type ACMEChallengeSolverHTTP01Istio struct {
|
||||
// Namespace of the gateway that is used to generate the virtualservice for.
|
||||
GatewayNamespace string `json:"gatewayNamespace"`
|
||||
|
||||
// Name of the gateway that is used to generate the virtualservice for.
|
||||
GatewayName string `json:"gatewayName"`
|
||||
|
||||
// Optional pod template used to configure the ACME challenge solver pods
|
||||
// used for HTTP01 challenges
|
||||
// +optional
|
||||
PodTemplate *ACMEChallengeSolverHTTP01IngressPodTemplate `json:"podTemplate,omitempty"`
|
||||
}
|
||||
|
||||
// Used to configure a DNS01 challenge provider to be used when solving DNS01
|
||||
// challenges.
|
||||
// Only one DNS provider may be configured per solver.
|
||||
|
||||
@@ -170,6 +170,11 @@ func (in *ACMEChallengeSolverHTTP01) DeepCopyInto(out *ACMEChallengeSolverHTTP01
|
||||
*out = new(ACMEChallengeSolverHTTP01Ingress)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Istio != nil {
|
||||
in, out := &in.Istio, &out.Istio
|
||||
*out = new(ACMEChallengeSolverHTTP01Istio)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -344,6 +349,27 @@ func (in *ACMEChallengeSolverHTTP01IngressTemplate) DeepCopy() *ACMEChallengeSol
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ACMEChallengeSolverHTTP01Istio) DeepCopyInto(out *ACMEChallengeSolverHTTP01Istio) {
|
||||
*out = *in
|
||||
if in.PodTemplate != nil {
|
||||
in, out := &in.PodTemplate, &out.PodTemplate
|
||||
*out = new(ACMEChallengeSolverHTTP01IngressPodTemplate)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01Istio.
|
||||
func (in *ACMEChallengeSolverHTTP01Istio) DeepCopy() *ACMEChallengeSolverHTTP01Istio {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ACMEChallengeSolverHTTP01Istio)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ACMEExternalAccountBinding) DeepCopyInto(out *ACMEExternalAccountBinding) {
|
||||
*out = *in
|
||||
|
||||
@@ -25,6 +25,8 @@ go_library(
|
||||
"@io_k8s_apimachinery//pkg/runtime/schema:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/util/runtime:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/util/wait:go_default_library",
|
||||
"@io_k8s_client_go//dynamic:go_default_library",
|
||||
"@io_k8s_client_go//dynamic/dynamicinformer:go_default_library",
|
||||
"@io_k8s_client_go//informers:go_default_library",
|
||||
"@io_k8s_client_go//kubernetes:go_default_library",
|
||||
"@io_k8s_client_go//rest:go_default_library",
|
||||
|
||||
@@ -25,6 +25,7 @@ go_library(
|
||||
"//pkg/issuer/acme/dns:go_default_library",
|
||||
"//pkg/issuer/acme/dns/util:go_default_library",
|
||||
"//pkg/issuer/acme/http:go_default_library",
|
||||
"//pkg/issuer/acme/http/internal/istio:go_default_library",
|
||||
"//pkg/logs:go_default_library",
|
||||
"//pkg/util/feature:go_default_library",
|
||||
"@com_github_go_logr_logr//:go_default_library",
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
"github.com/jetstack/cert-manager/pkg/issuer"
|
||||
"github.com/jetstack/cert-manager/pkg/issuer/acme/dns"
|
||||
"github.com/jetstack/cert-manager/pkg/issuer/acme/http"
|
||||
"github.com/jetstack/cert-manager/pkg/issuer/acme/http/internal/istio"
|
||||
logf "github.com/jetstack/cert-manager/pkg/logs"
|
||||
)
|
||||
|
||||
@@ -97,6 +98,7 @@ func (c *controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitin
|
||||
podInformer := ctx.KubeSharedInformerFactory.Core().V1().Pods()
|
||||
serviceInformer := ctx.KubeSharedInformerFactory.Core().V1().Services()
|
||||
ingressInformer := ctx.KubeSharedInformerFactory.Networking().V1beta1().Ingresses()
|
||||
|
||||
// build a list of InformerSynced functions that will be returned by the Register method.
|
||||
// the controller will only begin processing items once all of these informers have synced.
|
||||
mustSync := []cache.InformerSynced{
|
||||
@@ -108,6 +110,11 @@ func (c *controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitin
|
||||
ingressInformer.Informer().HasSynced,
|
||||
}
|
||||
|
||||
if ctx.IstioEnabled {
|
||||
virtualServiceInformer := ctx.DynamicSharedInformerFactory.ForResource(istio.VirtualServiceGvr())
|
||||
mustSync = append(mustSync, virtualServiceInformer.Informer().HasSynced)
|
||||
}
|
||||
|
||||
// set all the references to the listers for used by the Sync function
|
||||
c.challengeLister = challengeInformer.Lister()
|
||||
c.issuerLister = issuerInformer.Lister()
|
||||
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
dynamicclient "k8s.io/client-go/dynamic"
|
||||
dynamicinformers "k8s.io/client-go/dynamic/dynamicinformer"
|
||||
kubeinformers "k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
@@ -48,6 +50,8 @@ type Context struct {
|
||||
RESTConfig *rest.Config
|
||||
// Client is a Kubernetes clientset
|
||||
Client kubernetes.Interface
|
||||
// DynamicClient is a Dynamic clientset
|
||||
DynamicClient dynamicclient.Interface
|
||||
// CMClient is a cert-manager clientset
|
||||
CMClient clientset.Interface
|
||||
// Recorder to record events to
|
||||
@@ -56,10 +60,16 @@ type Context struct {
|
||||
// KubeSharedInformerFactory can be used to obtain shared
|
||||
// SharedIndexInformer instances for Kubernetes types
|
||||
KubeSharedInformerFactory kubeinformers.SharedInformerFactory
|
||||
// DynamicSharedInformerFactory can be used to obtain shared
|
||||
// SharedIndexInformer instances for Dynamic types
|
||||
DynamicSharedInformerFactory dynamicinformers.DynamicSharedInformerFactory
|
||||
// SharedInformerFactory can be used to obtain shared SharedIndexInformer
|
||||
// instances
|
||||
SharedInformerFactory informers.SharedInformerFactory
|
||||
|
||||
// IstioEnabled is true if Istio support is enabled
|
||||
IstioEnabled bool
|
||||
|
||||
// Namespace is the namespace to operate within.
|
||||
// If unset, operates on all namespaces
|
||||
Namespace string
|
||||
|
||||
@@ -23,7 +23,10 @@ go_library(
|
||||
"@com_github_kr_pretty//:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/runtime:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/runtime/schema:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/util/errors:go_default_library",
|
||||
"@io_k8s_client_go//dynamic/dynamicinformer:go_default_library",
|
||||
"@io_k8s_client_go//dynamic/fake:go_default_library",
|
||||
"@io_k8s_client_go//informers:go_default_library",
|
||||
"@io_k8s_client_go//kubernetes/fake:go_default_library",
|
||||
"@io_k8s_client_go//testing:go_default_library",
|
||||
|
||||
@@ -26,7 +26,11 @@ import (
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
dynamicinformers "k8s.io/client-go/dynamic/dynamicinformer"
|
||||
dynamicfake "k8s.io/client-go/dynamic/fake"
|
||||
kubeinformers "k8s.io/client-go/informers"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
coretesting "k8s.io/client-go/testing"
|
||||
@@ -57,6 +61,7 @@ type Builder struct {
|
||||
T *testing.T
|
||||
|
||||
KubeObjects []runtime.Object
|
||||
DynamicObjects []runtime.Object
|
||||
CertManagerObjects []runtime.Object
|
||||
ExpectedActions []Action
|
||||
ExpectedEvents []string
|
||||
@@ -106,12 +111,15 @@ func (b *Builder) Init() {
|
||||
}
|
||||
b.requiredReactors = make(map[string]bool)
|
||||
b.Client = kubefake.NewSimpleClientset(b.KubeObjects...)
|
||||
b.DynamicClient = dynamicfake.NewSimpleDynamicClient(runtime.NewScheme(), b.DynamicObjects...)
|
||||
b.CMClient = cmfake.NewSimpleClientset(b.CertManagerObjects...)
|
||||
b.Recorder = new(FakeRecorder)
|
||||
|
||||
b.FakeKubeClient().PrependReactor("create", "*", b.generateNameReactor)
|
||||
b.FakeDynamicClient().PrependReactor("create", "*", b.generateNameReactor)
|
||||
b.FakeCMClient().PrependReactor("create", "*", b.generateNameReactor)
|
||||
b.KubeSharedInformerFactory = kubeinformers.NewSharedInformerFactory(b.Client, informerResyncPeriod)
|
||||
b.DynamicSharedInformerFactory = dynamicinformers.NewDynamicSharedInformerFactory(b.DynamicClient, informerResyncPeriod)
|
||||
b.SharedInformerFactory = informers.NewSharedInformerFactory(b.CMClient, informerResyncPeriod)
|
||||
b.stopCh = make(chan struct{})
|
||||
b.Metrics = metrics.New(logs.Log)
|
||||
@@ -135,6 +143,14 @@ func (b *Builder) FakeKubeInformerFactory() kubeinformers.SharedInformerFactory
|
||||
return b.Context.KubeSharedInformerFactory
|
||||
}
|
||||
|
||||
func (b *Builder) FakeDynamicClient() *dynamicfake.FakeDynamicClient {
|
||||
return b.Context.DynamicClient.(*dynamicfake.FakeDynamicClient)
|
||||
}
|
||||
|
||||
func (b *Builder) FakeDynamicSharedInformerFactory() dynamicinformers.DynamicSharedInformerFactory {
|
||||
return b.Context.DynamicSharedInformerFactory
|
||||
}
|
||||
|
||||
func (b *Builder) FakeCMClient() *cmfake.Clientset {
|
||||
return b.Context.CMClient.(*cmfake.Clientset)
|
||||
}
|
||||
@@ -269,6 +285,7 @@ func (b *Builder) Stop() {
|
||||
|
||||
func (b *Builder) Start() {
|
||||
b.KubeSharedInformerFactory.Start(b.stopCh)
|
||||
b.DynamicSharedInformerFactory.Start(b.stopCh)
|
||||
b.SharedInformerFactory.Start(b.stopCh)
|
||||
// wait for caches to sync
|
||||
b.Sync()
|
||||
@@ -278,6 +295,9 @@ func (b *Builder) Sync() {
|
||||
if err := mustAllSync(b.KubeSharedInformerFactory.WaitForCacheSync(b.stopCh)); err != nil {
|
||||
panic("Error waiting for kubeSharedInformerFactory to sync: " + err.Error())
|
||||
}
|
||||
if err := mustAllSyncDynamic(b.DynamicSharedInformerFactory.WaitForCacheSync(b.stopCh)); err != nil {
|
||||
panic("Error waiting for dynamicSharedInformerFactory to sync: " + err.Error())
|
||||
}
|
||||
if err := mustAllSync(b.SharedInformerFactory.WaitForCacheSync(b.stopCh)); err != nil {
|
||||
panic("Error waiting for SharedInformerFactory to sync: " + err.Error())
|
||||
}
|
||||
@@ -304,6 +324,16 @@ func (b *Builder) Events() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func mustAllSyncDynamic(in map[schema.GroupVersionResource]bool) error {
|
||||
var errs []error
|
||||
for t, started := range in {
|
||||
if !started {
|
||||
errs = append(errs, fmt.Errorf("informer for %v not synced", t))
|
||||
}
|
||||
}
|
||||
return utilerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func mustAllSync(in map[reflect.Type]bool) error {
|
||||
var errs []error
|
||||
for t, started := range in {
|
||||
|
||||
@@ -186,6 +186,14 @@ type ACMEChallengeSolverHTTP01 struct {
|
||||
// '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are
|
||||
// provisioned by cert-manager for each Challenge to be completed.
|
||||
Ingress *ACMEChallengeSolverHTTP01Ingress
|
||||
|
||||
// The Istio virtualservice based HTTP01 challenge solver will solve
|
||||
// challenges by creating an Istio virtualservice resource that is connected
|
||||
// to the specified Istio gateway in order to route requests for
|
||||
// '/.well-known/acme-challenge/XYZ' to 'challenge solver' pods that are
|
||||
// provisioned by cert-manager for each Challenge to be completed.
|
||||
// +optional
|
||||
Istio *ACMEChallengeSolverHTTP01Istio `json:"istio,omitempty"`
|
||||
}
|
||||
|
||||
type ACMEChallengeSolverHTTP01Ingress struct {
|
||||
@@ -272,6 +280,19 @@ type ACMEChallengeSolverHTTP01IngressObjectMeta struct {
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
type ACMEChallengeSolverHTTP01Istio struct {
|
||||
// Namespace of the gateway that is used to generate the virtualservice for.
|
||||
GatewayNamespace string
|
||||
|
||||
// Name of the gateway that is used to generate the virtualservice for.
|
||||
GatewayName string
|
||||
|
||||
// Optional pod template used to configure the ACME challenge solver pods
|
||||
// used for HTTP01 challenges
|
||||
// +optional
|
||||
PodTemplate *ACMEChallengeSolverHTTP01IngressPodTemplate `json:"podTemplate,omitempty"`
|
||||
}
|
||||
|
||||
// Used to configure a DNS01 challenge provider to be used when solving DNS01
|
||||
// challenges.
|
||||
// Only one DNS provider may be configured per solver.
|
||||
|
||||
@@ -151,6 +151,16 @@ func RegisterConversions(s *runtime.Scheme) error {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*v1.ACMEChallengeSolverHTTP01Istio)(nil), (*acme.ACMEChallengeSolverHTTP01Istio)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(a.(*v1.ACMEChallengeSolverHTTP01Istio), b.(*acme.ACMEChallengeSolverHTTP01Istio), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*acme.ACMEChallengeSolverHTTP01Istio)(nil), (*v1.ACMEChallengeSolverHTTP01Istio)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_acme_ACMEChallengeSolverHTTP01Istio_To_v1_ACMEChallengeSolverHTTP01Istio(a.(*acme.ACMEChallengeSolverHTTP01Istio), b.(*v1.ACMEChallengeSolverHTTP01Istio), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*v1.ACMEExternalAccountBinding)(nil), (*acme.ACMEExternalAccountBinding)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_ACMEExternalAccountBinding_To_acme_ACMEExternalAccountBinding(a.(*v1.ACMEExternalAccountBinding), b.(*acme.ACMEExternalAccountBinding), scope)
|
||||
}); err != nil {
|
||||
@@ -480,6 +490,7 @@ func Convert_acme_ACMEChallengeSolverDNS01_To_v1_ACMEChallengeSolverDNS01(in *ac
|
||||
|
||||
func autoConvert_v1_ACMEChallengeSolverHTTP01_To_acme_ACMEChallengeSolverHTTP01(in *v1.ACMEChallengeSolverHTTP01, out *acme.ACMEChallengeSolverHTTP01, s conversion.Scope) error {
|
||||
out.Ingress = (*acme.ACMEChallengeSolverHTTP01Ingress)(unsafe.Pointer(in.Ingress))
|
||||
out.Istio = (*acme.ACMEChallengeSolverHTTP01Istio)(unsafe.Pointer(in.Istio))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -490,6 +501,7 @@ func Convert_v1_ACMEChallengeSolverHTTP01_To_acme_ACMEChallengeSolverHTTP01(in *
|
||||
|
||||
func autoConvert_acme_ACMEChallengeSolverHTTP01_To_v1_ACMEChallengeSolverHTTP01(in *acme.ACMEChallengeSolverHTTP01, out *v1.ACMEChallengeSolverHTTP01, s conversion.Scope) error {
|
||||
out.Ingress = (*v1.ACMEChallengeSolverHTTP01Ingress)(unsafe.Pointer(in.Ingress))
|
||||
out.Istio = (*v1.ACMEChallengeSolverHTTP01Istio)(unsafe.Pointer(in.Istio))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -652,6 +664,30 @@ func Convert_acme_ACMEChallengeSolverHTTP01IngressTemplate_To_v1_ACMEChallengeSo
|
||||
return autoConvert_acme_ACMEChallengeSolverHTTP01IngressTemplate_To_v1_ACMEChallengeSolverHTTP01IngressTemplate(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(in *v1.ACMEChallengeSolverHTTP01Istio, out *acme.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
out.GatewayNamespace = in.GatewayNamespace
|
||||
out.GatewayName = in.GatewayName
|
||||
out.PodTemplate = (*acme.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio is an autogenerated conversion function.
|
||||
func Convert_v1_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(in *v1.ACMEChallengeSolverHTTP01Istio, out *acme.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
return autoConvert_v1_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_acme_ACMEChallengeSolverHTTP01Istio_To_v1_ACMEChallengeSolverHTTP01Istio(in *acme.ACMEChallengeSolverHTTP01Istio, out *v1.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
out.GatewayNamespace = in.GatewayNamespace
|
||||
out.GatewayName = in.GatewayName
|
||||
out.PodTemplate = (*v1.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_acme_ACMEChallengeSolverHTTP01Istio_To_v1_ACMEChallengeSolverHTTP01Istio is an autogenerated conversion function.
|
||||
func Convert_acme_ACMEChallengeSolverHTTP01Istio_To_v1_ACMEChallengeSolverHTTP01Istio(in *acme.ACMEChallengeSolverHTTP01Istio, out *v1.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
return autoConvert_acme_ACMEChallengeSolverHTTP01Istio_To_v1_ACMEChallengeSolverHTTP01Istio(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_ACMEExternalAccountBinding_To_acme_ACMEExternalAccountBinding(in *v1.ACMEExternalAccountBinding, out *acme.ACMEExternalAccountBinding, s conversion.Scope) error {
|
||||
out.KeyID = in.KeyID
|
||||
// TODO: Inefficient conversion - can we improve it?
|
||||
|
||||
@@ -151,6 +151,16 @@ func RegisterConversions(s *runtime.Scheme) error {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*v1alpha2.ACMEChallengeSolverHTTP01Istio)(nil), (*acme.ACMEChallengeSolverHTTP01Istio)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha2_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(a.(*v1alpha2.ACMEChallengeSolverHTTP01Istio), b.(*acme.ACMEChallengeSolverHTTP01Istio), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*acme.ACMEChallengeSolverHTTP01Istio)(nil), (*v1alpha2.ACMEChallengeSolverHTTP01Istio)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_acme_ACMEChallengeSolverHTTP01Istio_To_v1alpha2_ACMEChallengeSolverHTTP01Istio(a.(*acme.ACMEChallengeSolverHTTP01Istio), b.(*v1alpha2.ACMEChallengeSolverHTTP01Istio), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*v1alpha2.ACMEExternalAccountBinding)(nil), (*acme.ACMEExternalAccountBinding)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha2_ACMEExternalAccountBinding_To_acme_ACMEExternalAccountBinding(a.(*v1alpha2.ACMEExternalAccountBinding), b.(*acme.ACMEExternalAccountBinding), scope)
|
||||
}); err != nil {
|
||||
@@ -480,6 +490,7 @@ func Convert_acme_ACMEChallengeSolverDNS01_To_v1alpha2_ACMEChallengeSolverDNS01(
|
||||
|
||||
func autoConvert_v1alpha2_ACMEChallengeSolverHTTP01_To_acme_ACMEChallengeSolverHTTP01(in *v1alpha2.ACMEChallengeSolverHTTP01, out *acme.ACMEChallengeSolverHTTP01, s conversion.Scope) error {
|
||||
out.Ingress = (*acme.ACMEChallengeSolverHTTP01Ingress)(unsafe.Pointer(in.Ingress))
|
||||
out.Istio = (*acme.ACMEChallengeSolverHTTP01Istio)(unsafe.Pointer(in.Istio))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -490,6 +501,7 @@ func Convert_v1alpha2_ACMEChallengeSolverHTTP01_To_acme_ACMEChallengeSolverHTTP0
|
||||
|
||||
func autoConvert_acme_ACMEChallengeSolverHTTP01_To_v1alpha2_ACMEChallengeSolverHTTP01(in *acme.ACMEChallengeSolverHTTP01, out *v1alpha2.ACMEChallengeSolverHTTP01, s conversion.Scope) error {
|
||||
out.Ingress = (*v1alpha2.ACMEChallengeSolverHTTP01Ingress)(unsafe.Pointer(in.Ingress))
|
||||
out.Istio = (*v1alpha2.ACMEChallengeSolverHTTP01Istio)(unsafe.Pointer(in.Istio))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -652,6 +664,30 @@ func Convert_acme_ACMEChallengeSolverHTTP01IngressTemplate_To_v1alpha2_ACMEChall
|
||||
return autoConvert_acme_ACMEChallengeSolverHTTP01IngressTemplate_To_v1alpha2_ACMEChallengeSolverHTTP01IngressTemplate(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha2_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(in *v1alpha2.ACMEChallengeSolverHTTP01Istio, out *acme.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
out.GatewayNamespace = in.GatewayNamespace
|
||||
out.GatewayName = in.GatewayName
|
||||
out.PodTemplate = (*acme.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha2_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio is an autogenerated conversion function.
|
||||
func Convert_v1alpha2_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(in *v1alpha2.ACMEChallengeSolverHTTP01Istio, out *acme.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha2_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_acme_ACMEChallengeSolverHTTP01Istio_To_v1alpha2_ACMEChallengeSolverHTTP01Istio(in *acme.ACMEChallengeSolverHTTP01Istio, out *v1alpha2.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
out.GatewayNamespace = in.GatewayNamespace
|
||||
out.GatewayName = in.GatewayName
|
||||
out.PodTemplate = (*v1alpha2.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_acme_ACMEChallengeSolverHTTP01Istio_To_v1alpha2_ACMEChallengeSolverHTTP01Istio is an autogenerated conversion function.
|
||||
func Convert_acme_ACMEChallengeSolverHTTP01Istio_To_v1alpha2_ACMEChallengeSolverHTTP01Istio(in *acme.ACMEChallengeSolverHTTP01Istio, out *v1alpha2.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
return autoConvert_acme_ACMEChallengeSolverHTTP01Istio_To_v1alpha2_ACMEChallengeSolverHTTP01Istio(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha2_ACMEExternalAccountBinding_To_acme_ACMEExternalAccountBinding(in *v1alpha2.ACMEExternalAccountBinding, out *acme.ACMEExternalAccountBinding, s conversion.Scope) error {
|
||||
out.KeyID = in.KeyID
|
||||
// TODO: Inefficient conversion - can we improve it?
|
||||
|
||||
@@ -151,6 +151,16 @@ func RegisterConversions(s *runtime.Scheme) error {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*v1alpha3.ACMEChallengeSolverHTTP01Istio)(nil), (*acme.ACMEChallengeSolverHTTP01Istio)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha3_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(a.(*v1alpha3.ACMEChallengeSolverHTTP01Istio), b.(*acme.ACMEChallengeSolverHTTP01Istio), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*acme.ACMEChallengeSolverHTTP01Istio)(nil), (*v1alpha3.ACMEChallengeSolverHTTP01Istio)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_acme_ACMEChallengeSolverHTTP01Istio_To_v1alpha3_ACMEChallengeSolverHTTP01Istio(a.(*acme.ACMEChallengeSolverHTTP01Istio), b.(*v1alpha3.ACMEChallengeSolverHTTP01Istio), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*v1alpha3.ACMEExternalAccountBinding)(nil), (*acme.ACMEExternalAccountBinding)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha3_ACMEExternalAccountBinding_To_acme_ACMEExternalAccountBinding(a.(*v1alpha3.ACMEExternalAccountBinding), b.(*acme.ACMEExternalAccountBinding), scope)
|
||||
}); err != nil {
|
||||
@@ -480,6 +490,7 @@ func Convert_acme_ACMEChallengeSolverDNS01_To_v1alpha3_ACMEChallengeSolverDNS01(
|
||||
|
||||
func autoConvert_v1alpha3_ACMEChallengeSolverHTTP01_To_acme_ACMEChallengeSolverHTTP01(in *v1alpha3.ACMEChallengeSolverHTTP01, out *acme.ACMEChallengeSolverHTTP01, s conversion.Scope) error {
|
||||
out.Ingress = (*acme.ACMEChallengeSolverHTTP01Ingress)(unsafe.Pointer(in.Ingress))
|
||||
out.Istio = (*acme.ACMEChallengeSolverHTTP01Istio)(unsafe.Pointer(in.Istio))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -490,6 +501,7 @@ func Convert_v1alpha3_ACMEChallengeSolverHTTP01_To_acme_ACMEChallengeSolverHTTP0
|
||||
|
||||
func autoConvert_acme_ACMEChallengeSolverHTTP01_To_v1alpha3_ACMEChallengeSolverHTTP01(in *acme.ACMEChallengeSolverHTTP01, out *v1alpha3.ACMEChallengeSolverHTTP01, s conversion.Scope) error {
|
||||
out.Ingress = (*v1alpha3.ACMEChallengeSolverHTTP01Ingress)(unsafe.Pointer(in.Ingress))
|
||||
out.Istio = (*v1alpha3.ACMEChallengeSolverHTTP01Istio)(unsafe.Pointer(in.Istio))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -652,6 +664,30 @@ func Convert_acme_ACMEChallengeSolverHTTP01IngressTemplate_To_v1alpha3_ACMEChall
|
||||
return autoConvert_acme_ACMEChallengeSolverHTTP01IngressTemplate_To_v1alpha3_ACMEChallengeSolverHTTP01IngressTemplate(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha3_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(in *v1alpha3.ACMEChallengeSolverHTTP01Istio, out *acme.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
out.GatewayNamespace = in.GatewayNamespace
|
||||
out.GatewayName = in.GatewayName
|
||||
out.PodTemplate = (*acme.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha3_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio is an autogenerated conversion function.
|
||||
func Convert_v1alpha3_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(in *v1alpha3.ACMEChallengeSolverHTTP01Istio, out *acme.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha3_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_acme_ACMEChallengeSolverHTTP01Istio_To_v1alpha3_ACMEChallengeSolverHTTP01Istio(in *acme.ACMEChallengeSolverHTTP01Istio, out *v1alpha3.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
out.GatewayNamespace = in.GatewayNamespace
|
||||
out.GatewayName = in.GatewayName
|
||||
out.PodTemplate = (*v1alpha3.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_acme_ACMEChallengeSolverHTTP01Istio_To_v1alpha3_ACMEChallengeSolverHTTP01Istio is an autogenerated conversion function.
|
||||
func Convert_acme_ACMEChallengeSolverHTTP01Istio_To_v1alpha3_ACMEChallengeSolverHTTP01Istio(in *acme.ACMEChallengeSolverHTTP01Istio, out *v1alpha3.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
return autoConvert_acme_ACMEChallengeSolverHTTP01Istio_To_v1alpha3_ACMEChallengeSolverHTTP01Istio(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha3_ACMEExternalAccountBinding_To_acme_ACMEExternalAccountBinding(in *v1alpha3.ACMEExternalAccountBinding, out *acme.ACMEExternalAccountBinding, s conversion.Scope) error {
|
||||
out.KeyID = in.KeyID
|
||||
// TODO: Inefficient conversion - can we improve it?
|
||||
|
||||
@@ -151,6 +151,16 @@ func RegisterConversions(s *runtime.Scheme) error {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*v1beta1.ACMEChallengeSolverHTTP01Istio)(nil), (*acme.ACMEChallengeSolverHTTP01Istio)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1beta1_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(a.(*v1beta1.ACMEChallengeSolverHTTP01Istio), b.(*acme.ACMEChallengeSolverHTTP01Istio), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*acme.ACMEChallengeSolverHTTP01Istio)(nil), (*v1beta1.ACMEChallengeSolverHTTP01Istio)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_acme_ACMEChallengeSolverHTTP01Istio_To_v1beta1_ACMEChallengeSolverHTTP01Istio(a.(*acme.ACMEChallengeSolverHTTP01Istio), b.(*v1beta1.ACMEChallengeSolverHTTP01Istio), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*v1beta1.ACMEExternalAccountBinding)(nil), (*acme.ACMEExternalAccountBinding)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1beta1_ACMEExternalAccountBinding_To_acme_ACMEExternalAccountBinding(a.(*v1beta1.ACMEExternalAccountBinding), b.(*acme.ACMEExternalAccountBinding), scope)
|
||||
}); err != nil {
|
||||
@@ -480,6 +490,7 @@ func Convert_acme_ACMEChallengeSolverDNS01_To_v1beta1_ACMEChallengeSolverDNS01(i
|
||||
|
||||
func autoConvert_v1beta1_ACMEChallengeSolverHTTP01_To_acme_ACMEChallengeSolverHTTP01(in *v1beta1.ACMEChallengeSolverHTTP01, out *acme.ACMEChallengeSolverHTTP01, s conversion.Scope) error {
|
||||
out.Ingress = (*acme.ACMEChallengeSolverHTTP01Ingress)(unsafe.Pointer(in.Ingress))
|
||||
out.Istio = (*acme.ACMEChallengeSolverHTTP01Istio)(unsafe.Pointer(in.Istio))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -490,6 +501,7 @@ func Convert_v1beta1_ACMEChallengeSolverHTTP01_To_acme_ACMEChallengeSolverHTTP01
|
||||
|
||||
func autoConvert_acme_ACMEChallengeSolverHTTP01_To_v1beta1_ACMEChallengeSolverHTTP01(in *acme.ACMEChallengeSolverHTTP01, out *v1beta1.ACMEChallengeSolverHTTP01, s conversion.Scope) error {
|
||||
out.Ingress = (*v1beta1.ACMEChallengeSolverHTTP01Ingress)(unsafe.Pointer(in.Ingress))
|
||||
out.Istio = (*v1beta1.ACMEChallengeSolverHTTP01Istio)(unsafe.Pointer(in.Istio))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -652,6 +664,30 @@ func Convert_acme_ACMEChallengeSolverHTTP01IngressTemplate_To_v1beta1_ACMEChalle
|
||||
return autoConvert_acme_ACMEChallengeSolverHTTP01IngressTemplate_To_v1beta1_ACMEChallengeSolverHTTP01IngressTemplate(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1beta1_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(in *v1beta1.ACMEChallengeSolverHTTP01Istio, out *acme.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
out.GatewayNamespace = in.GatewayNamespace
|
||||
out.GatewayName = in.GatewayName
|
||||
out.PodTemplate = (*acme.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1beta1_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio is an autogenerated conversion function.
|
||||
func Convert_v1beta1_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(in *v1beta1.ACMEChallengeSolverHTTP01Istio, out *acme.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
return autoConvert_v1beta1_ACMEChallengeSolverHTTP01Istio_To_acme_ACMEChallengeSolverHTTP01Istio(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_acme_ACMEChallengeSolverHTTP01Istio_To_v1beta1_ACMEChallengeSolverHTTP01Istio(in *acme.ACMEChallengeSolverHTTP01Istio, out *v1beta1.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
out.GatewayNamespace = in.GatewayNamespace
|
||||
out.GatewayName = in.GatewayName
|
||||
out.PodTemplate = (*v1beta1.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_acme_ACMEChallengeSolverHTTP01Istio_To_v1beta1_ACMEChallengeSolverHTTP01Istio is an autogenerated conversion function.
|
||||
func Convert_acme_ACMEChallengeSolverHTTP01Istio_To_v1beta1_ACMEChallengeSolverHTTP01Istio(in *acme.ACMEChallengeSolverHTTP01Istio, out *v1beta1.ACMEChallengeSolverHTTP01Istio, s conversion.Scope) error {
|
||||
return autoConvert_acme_ACMEChallengeSolverHTTP01Istio_To_v1beta1_ACMEChallengeSolverHTTP01Istio(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1beta1_ACMEExternalAccountBinding_To_acme_ACMEExternalAccountBinding(in *v1beta1.ACMEExternalAccountBinding, out *acme.ACMEExternalAccountBinding, s conversion.Scope) error {
|
||||
out.KeyID = in.KeyID
|
||||
// TODO: Inefficient conversion - can we improve it?
|
||||
|
||||
@@ -170,6 +170,11 @@ func (in *ACMEChallengeSolverHTTP01) DeepCopyInto(out *ACMEChallengeSolverHTTP01
|
||||
*out = new(ACMEChallengeSolverHTTP01Ingress)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Istio != nil {
|
||||
in, out := &in.Istio, &out.Istio
|
||||
*out = new(ACMEChallengeSolverHTTP01Istio)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -344,6 +349,27 @@ func (in *ACMEChallengeSolverHTTP01IngressTemplate) DeepCopy() *ACMEChallengeSol
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ACMEChallengeSolverHTTP01Istio) DeepCopyInto(out *ACMEChallengeSolverHTTP01Istio) {
|
||||
*out = *in
|
||||
if in.PodTemplate != nil {
|
||||
in, out := &in.PodTemplate, &out.PodTemplate
|
||||
*out = new(ACMEChallengeSolverHTTP01IngressPodTemplate)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01Istio.
|
||||
func (in *ACMEChallengeSolverHTTP01Istio) DeepCopy() *ACMEChallengeSolverHTTP01Istio {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ACMEChallengeSolverHTTP01Istio)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ACMEExternalAccountBinding) DeepCopyInto(out *ACMEExternalAccountBinding) {
|
||||
*out = *in
|
||||
|
||||
@@ -156,8 +156,11 @@ func ValidateACMEIssuerChallengeSolverHTTP01Config(http01 *cmacme.ACMEChallengeS
|
||||
numDefined++
|
||||
el = append(el, ValidateACMEIssuerChallengeSolverHTTP01IngressConfig(http01.Ingress, fldPath.Child("ingress"))...)
|
||||
}
|
||||
if numDefined == 0 {
|
||||
el = append(el, field.Required(fldPath, "no HTTP01 solver type configured"))
|
||||
if http01.Istio != nil {
|
||||
numDefined++
|
||||
}
|
||||
if numDefined != 1 {
|
||||
el = append(el, field.Required(fldPath, "exactly 1 HTTP01 solver type has to be configured"))
|
||||
}
|
||||
|
||||
return el
|
||||
|
||||
@@ -215,7 +215,7 @@ func TestValidateACMEIssuerConfig(t *testing.T) {
|
||||
},
|
||||
},
|
||||
errs: []*field.Error{
|
||||
field.Required(fldPath.Child("solvers").Index(0).Child("http01"), "no HTTP01 solver type configured"),
|
||||
field.Required(fldPath.Child("solvers").Index(0).Child("http01"), "exactly 1 HTTP01 solver type has to be configured"),
|
||||
},
|
||||
},
|
||||
"acme solver with valid http01 config": {
|
||||
@@ -458,7 +458,7 @@ func TestValidateACMEIssuerHTTP01Config(t *testing.T) {
|
||||
"no solver config type specified": {
|
||||
cfg: &cmacme.ACMEChallengeSolverHTTP01{},
|
||||
errs: []*field.Error{
|
||||
field.Required(fldPath, "no HTTP01 solver type configured"),
|
||||
field.Required(fldPath, "exactly 1 HTTP01 solver type has to be configured"),
|
||||
},
|
||||
},
|
||||
"both fields specified": {
|
||||
|
||||
@@ -5,6 +5,7 @@ go_library(
|
||||
srcs = [
|
||||
"http.go",
|
||||
"ingress.go",
|
||||
"istio.go",
|
||||
"pod.go",
|
||||
"service.go",
|
||||
],
|
||||
@@ -14,9 +15,11 @@ go_library(
|
||||
"//pkg/apis/acme/v1:go_default_library",
|
||||
"//pkg/apis/certmanager/v1:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/issuer/acme/http/internal/istio:go_default_library",
|
||||
"//pkg/issuer/acme/http/solver:go_default_library",
|
||||
"//pkg/logs:go_default_library",
|
||||
"//pkg/util:go_default_library",
|
||||
"@io_istio_api//networking/v1beta1:go_default_library",
|
||||
"@io_k8s_api//core/v1:go_default_library",
|
||||
"@io_k8s_api//networking/v1beta1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/api/errors:go_default_library",
|
||||
@@ -25,6 +28,7 @@ go_library(
|
||||
"@io_k8s_apimachinery//pkg/selection:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/util/errors:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/util/intstr:go_default_library",
|
||||
"@io_k8s_client_go//dynamic/dynamiclister:go_default_library",
|
||||
"@io_k8s_client_go//listers/core/v1:go_default_library",
|
||||
"@io_k8s_client_go//listers/networking/v1beta1:go_default_library",
|
||||
"@io_k8s_utils//net:go_default_library",
|
||||
@@ -36,6 +40,7 @@ go_test(
|
||||
srcs = [
|
||||
"http_test.go",
|
||||
"ingress_test.go",
|
||||
"istio_test.go",
|
||||
"pod_test.go",
|
||||
"service_test.go",
|
||||
"util_test.go",
|
||||
@@ -44,6 +49,7 @@ go_test(
|
||||
deps = [
|
||||
"//pkg/apis/acme/v1:go_default_library",
|
||||
"//pkg/controller/test:go_default_library",
|
||||
"//pkg/issuer/acme/http/internal/istio:go_default_library",
|
||||
"//test/unit/gen:go_default_library",
|
||||
"@io_k8s_api//core/v1:go_default_library",
|
||||
"@io_k8s_api//networking/v1beta1:go_default_library",
|
||||
@@ -68,6 +74,7 @@ filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/issuer/acme/http/internal/istio:all-srcs",
|
||||
"//pkg/issuer/acme/http/solver:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
|
||||
@@ -19,6 +19,7 @@ package http
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
@@ -29,13 +30,16 @@ import (
|
||||
|
||||
k8snet "k8s.io/utils/net"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/client-go/dynamic/dynamiclister"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
networkingv1beta1listers "k8s.io/client-go/listers/networking/v1beta1"
|
||||
|
||||
cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1"
|
||||
v1 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
|
||||
"github.com/jetstack/cert-manager/pkg/controller"
|
||||
"github.com/jetstack/cert-manager/pkg/issuer/acme/http/internal/istio"
|
||||
"github.com/jetstack/cert-manager/pkg/issuer/acme/http/solver"
|
||||
logf "github.com/jetstack/cert-manager/pkg/logs"
|
||||
pkgutil "github.com/jetstack/cert-manager/pkg/util"
|
||||
@@ -57,9 +61,10 @@ var (
|
||||
type Solver struct {
|
||||
*controller.Context
|
||||
|
||||
podLister corev1listers.PodLister
|
||||
serviceLister corev1listers.ServiceLister
|
||||
ingressLister networkingv1beta1listers.IngressLister
|
||||
podLister corev1listers.PodLister
|
||||
serviceLister corev1listers.ServiceLister
|
||||
ingressLister networkingv1beta1listers.IngressLister
|
||||
virtualServiceLister dynamiclister.Lister
|
||||
|
||||
testReachability reachabilityTest
|
||||
requiredPasses int
|
||||
@@ -70,13 +75,15 @@ type reachabilityTest func(ctx context.Context, url *url.URL, key string) error
|
||||
// NewSolver returns a new ACME HTTP01 solver for the given Issuer and client.
|
||||
// TODO: refactor this to have fewer args
|
||||
func NewSolver(ctx *controller.Context) *Solver {
|
||||
dynamicInformer := ctx.DynamicSharedInformerFactory.ForResource(istio.VirtualServiceGvr())
|
||||
return &Solver{
|
||||
Context: ctx,
|
||||
podLister: ctx.KubeSharedInformerFactory.Core().V1().Pods().Lister(),
|
||||
serviceLister: ctx.KubeSharedInformerFactory.Core().V1().Services().Lister(),
|
||||
ingressLister: ctx.KubeSharedInformerFactory.Networking().V1beta1().Ingresses().Lister(),
|
||||
testReachability: testReachability,
|
||||
requiredPasses: 5,
|
||||
Context: ctx,
|
||||
podLister: ctx.KubeSharedInformerFactory.Core().V1().Pods().Lister(),
|
||||
serviceLister: ctx.KubeSharedInformerFactory.Core().V1().Services().Lister(),
|
||||
ingressLister: ctx.KubeSharedInformerFactory.Networking().V1beta1().Ingresses().Lister(),
|
||||
virtualServiceLister: dynamiclister.New(dynamicInformer.Informer().GetIndexer(), istio.VirtualServiceGvr()),
|
||||
testReachability: testReachability,
|
||||
requiredPasses: 5,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,14 +91,27 @@ func http01LogCtx(ctx context.Context) context.Context {
|
||||
return logf.NewContext(ctx, nil, "http01")
|
||||
}
|
||||
|
||||
func httpDomainCfgForChallenge(ch *cmacme.Challenge) (*cmacme.ACMEChallengeSolverHTTP01Ingress, error) {
|
||||
func httpIngressForChallenge(ch *cmacme.Challenge) (*cmacme.ACMEChallengeSolverHTTP01Ingress, error) {
|
||||
if ch.Spec.Solver.HTTP01 == nil || ch.Spec.Solver.HTTP01.Ingress == nil {
|
||||
return nil, fmt.Errorf("challenge's 'solver' field is specified but no HTTP01 ingress config provided. " +
|
||||
return nil, errors.New("challenge's 'solver' field is specified but no HTTP01 ingress config provided. " +
|
||||
"Ensure solvers[].http01.ingress is specified on your issuer resource")
|
||||
}
|
||||
return ch.Spec.Solver.HTTP01.Ingress, nil
|
||||
}
|
||||
|
||||
func serviceTypeForChallenge(ch *cmacme.Challenge) (corev1.ServiceType, error) {
|
||||
if ch.Spec.Solver.HTTP01 != nil {
|
||||
if ch.Spec.Solver.HTTP01.Ingress != nil {
|
||||
return ch.Spec.Solver.HTTP01.Ingress.ServiceType, nil
|
||||
}
|
||||
if ch.Spec.Solver.HTTP01.Istio != nil {
|
||||
return corev1.ServiceTypeClusterIP, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("could not determine service type for challenge")
|
||||
}
|
||||
|
||||
// Present will realise the resources required to solve the given HTTP01
|
||||
// challenge validation in the apiserver. If those resources already exist, it
|
||||
// will return nil (i.e. this function is idempotent).
|
||||
@@ -103,8 +123,22 @@ func (s *Solver) Present(ctx context.Context, issuer v1.GenericIssuer, ch *cmacm
|
||||
if svcErr != nil {
|
||||
return utilerrors.NewAggregate([]error{podErr, svcErr})
|
||||
}
|
||||
_, ingressErr := s.ensureIngress(ctx, ch, svc.Name)
|
||||
return utilerrors.NewAggregate([]error{podErr, svcErr, ingressErr})
|
||||
|
||||
var ingressErr error
|
||||
if ch.Spec.Solver.HTTP01.Ingress != nil {
|
||||
_, ingressErr = s.ensureIngress(ctx, ch, svc.Name)
|
||||
}
|
||||
|
||||
var istioErr error
|
||||
if ch.Spec.Solver.HTTP01.Istio != nil {
|
||||
if s.IstioEnabled {
|
||||
_, istioErr = s.ensureIstio(ctx, ch, svc.Name)
|
||||
} else {
|
||||
istioErr = errors.New("Istio support was not detected on startup, try restarting cert-manager")
|
||||
}
|
||||
}
|
||||
|
||||
return utilerrors.NewAggregate([]error{podErr, svcErr, ingressErr, istioErr})
|
||||
}
|
||||
|
||||
func (s *Solver) Check(ctx context.Context, issuer v1.GenericIssuer, ch *cmacme.Challenge) error {
|
||||
@@ -116,7 +150,7 @@ func (s *Solver) Check(ctx context.Context, issuer v1.GenericIssuer, ch *cmacme.
|
||||
// Call present again to be certain.
|
||||
// if the listers are nil, that means we're in the present checks
|
||||
// test
|
||||
if s.podLister != nil && s.serviceLister != nil && s.ingressLister != nil {
|
||||
if s.podLister != nil && s.serviceLister != nil && s.ingressLister != nil && s.virtualServiceLister != nil {
|
||||
log.V(logf.DebugLevel).Info("calling Present function before running self check to ensure required resources exist")
|
||||
err := s.Present(ctx, issuer, ch)
|
||||
if err != nil {
|
||||
@@ -153,6 +187,7 @@ func (s *Solver) CleanUp(ctx context.Context, issuer v1.GenericIssuer, ch *cmacm
|
||||
errs = append(errs, s.cleanupPods(ctx, ch))
|
||||
errs = append(errs, s.cleanupServices(ctx, ch))
|
||||
errs = append(errs, s.cleanupIngresses(ctx, ch))
|
||||
errs = append(errs, s.cleanupVirtualServices(ctx, ch))
|
||||
return utilerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ func (s *Solver) getIngressesForChallenge(ctx context.Context, ch *cmacme.Challe
|
||||
// that the ingress has an appropriate challenge path configured
|
||||
func (s *Solver) ensureIngress(ctx context.Context, ch *cmacme.Challenge, svcName string) (ing *networkingv1beta1.Ingress, err error) {
|
||||
log := logf.FromContext(ctx).WithName("ensureIngress")
|
||||
httpDomainCfg, err := httpDomainCfgForChallenge(ch)
|
||||
httpDomainCfg, err := httpIngressForChallenge(ch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -135,7 +135,7 @@ func (s *Solver) createIngress(ctx context.Context, ch *cmacme.Challenge, svcNam
|
||||
}
|
||||
|
||||
func buildIngressResource(ch *cmacme.Challenge, svcName string) (*networkingv1beta1.Ingress, error) {
|
||||
httpDomainCfg, err := httpDomainCfgForChallenge(ch)
|
||||
httpDomainCfg, err := httpIngressForChallenge(ch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -211,7 +211,7 @@ func (s *Solver) mergeIngressObjectMetaWithIngressResourceTemplate(ingress *netw
|
||||
}
|
||||
|
||||
func (s *Solver) addChallengePathToIngress(ctx context.Context, ch *cmacme.Challenge, svcName string) (*networkingv1beta1.Ingress, error) {
|
||||
httpDomainCfg, err := httpDomainCfgForChallenge(ch)
|
||||
httpDomainCfg, err := httpIngressForChallenge(ch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -265,7 +265,12 @@ func (s *Solver) addChallengePathToIngress(ctx context.Context, ch *cmacme.Chall
|
||||
func (s *Solver) cleanupIngresses(ctx context.Context, ch *cmacme.Challenge) error {
|
||||
log := logf.FromContext(ctx, "cleanupPods")
|
||||
|
||||
httpDomainCfg, err := httpDomainCfgForChallenge(ch)
|
||||
// Only do cleanup if HTTP01 and Ingress are set
|
||||
if ch.Spec.Solver.HTTP01 == nil || ch.Spec.Solver.HTTP01.Ingress == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
httpDomainCfg, err := httpIngressForChallenge(ch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"istio.go",
|
||||
"istio_deepcopy.gen.go",
|
||||
],
|
||||
importpath = "github.com/jetstack/cert-manager/pkg/issuer/acme/http/internal/istio",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"@io_istio_api//meta/v1alpha1:go_default_library",
|
||||
"@io_istio_api//networking/v1beta1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/runtime:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/runtime/schema:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// By utilising this file, no dependance on `istio.io/client-go` is
|
||||
// required. Used in combination with a dynamic client, it is possible
|
||||
// to create a client for these CRDs without adding a client-go dependency.
|
||||
|
||||
package istio
|
||||
|
||||
import (
|
||||
v1alpha1 "istio.io/api/meta/v1alpha1"
|
||||
networkingv1beta1 "istio.io/api/networking/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
var (
|
||||
virtualServiceGvk = schema.GroupVersionKind{Group: "networking.istio.io", Version: "v1beta1", Kind: "VirtualService"}
|
||||
virtualServiceGvr = schema.GroupVersionResource{Group: "networking.istio.io", Version: "v1beta1", Resource: "virtualservices"}
|
||||
)
|
||||
|
||||
func VirtualServiceGvr() schema.GroupVersionResource {
|
||||
return virtualServiceGvr
|
||||
}
|
||||
|
||||
type VirtualService struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec defines the implementation of this definition.
|
||||
Spec networkingv1beta1.VirtualService `json:"spec,omitempty"`
|
||||
|
||||
Status v1alpha1.IstioStatus `json:"status"`
|
||||
}
|
||||
|
||||
// VirtualServiceList is a collection of VirtualServices.
|
||||
type VirtualServiceList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []VirtualService `json:"items"`
|
||||
}
|
||||
|
||||
func (virtualService *VirtualService) ToUnstructured() (*unstructured.Unstructured, error) {
|
||||
virtualService.TypeMeta.SetGroupVersionKind(virtualServiceGvk)
|
||||
unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(virtualService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &unstructured.Unstructured{Object: unstructuredObj}, nil
|
||||
}
|
||||
|
||||
func VirtualServiceFromUnstructured(unstr *unstructured.Unstructured) (*VirtualService, error) {
|
||||
var virtualService VirtualService
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstr.UnstructuredContent(), &virtualService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &virtualService, nil
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// By utilising this file, no dependance on `istio.io/client-go` is
|
||||
// required. Used in combination with a dynamic client, it is possible
|
||||
// to create a client for these CRDs without adding a client-go dependency.
|
||||
|
||||
package istio
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *VirtualService) DeepCopyInto(out *VirtualService) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualService.
|
||||
func (in *VirtualService) DeepCopy() *VirtualService {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(VirtualService)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *VirtualService) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *VirtualServiceList) DeepCopyInto(out *VirtualServiceList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]VirtualService, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualServiceList.
|
||||
func (in *VirtualServiceList) DeepCopy() *VirtualServiceList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(VirtualServiceList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *VirtualServiceList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
networkingv1beta1 "istio.io/api/networking/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1"
|
||||
"github.com/jetstack/cert-manager/pkg/issuer/acme/http/internal/istio"
|
||||
logf "github.com/jetstack/cert-manager/pkg/logs"
|
||||
)
|
||||
|
||||
func (s *Solver) ensureIstio(ctx context.Context, ch *cmacme.Challenge, svcName string) (*istio.VirtualService, error) {
|
||||
log := logf.FromContext(ctx).WithName("ensureIstio")
|
||||
|
||||
virtualService, err := s.getVirtualService(ctx, ch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if virtualService == nil {
|
||||
log.Info("creating VirtualService")
|
||||
virtualService, err = s.createVirtualService(ctx, ch, svcName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Info("created VirtualService successfully")
|
||||
|
||||
return virtualService, nil
|
||||
}
|
||||
|
||||
log.Info("found VirtualService")
|
||||
|
||||
virtualService, err = s.checkAndUpdateVirtualService(ctx, ch, svcName, virtualService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return virtualService, nil
|
||||
}
|
||||
|
||||
func (s *Solver) cleanupVirtualServices(_ context.Context, _ *cmacme.Challenge) error {
|
||||
// Nothing to do, GC will take care of deleting the VirtualServices when the Challenge is deleted
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Solver) getVirtualService(ctx context.Context, ch *cmacme.Challenge) (*istio.VirtualService, error) {
|
||||
log := logf.FromContext(ctx, "getVirtualService")
|
||||
|
||||
selector := labels.Set(podLabels(ch)).AsSelector()
|
||||
vsList, err := s.virtualServiceLister.Namespace(ch.Namespace).List(selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch len(vsList) {
|
||||
case 0:
|
||||
return nil, nil
|
||||
case 1:
|
||||
virtualService, err := istio.VirtualServiceFromUnstructured(vsList[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return virtualService, nil
|
||||
default:
|
||||
for _, vs := range vsList[1:] {
|
||||
log.Info("deleting VirtualService")
|
||||
err := s.DynamicClient.Resource(istio.VirtualServiceGvr()).Namespace(ch.Namespace).Delete(ctx, vs.GetName(), metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("multiple VirtualServices found")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Solver) createVirtualService(ctx context.Context, ch *cmacme.Challenge, svcName string) (*istio.VirtualService, error) {
|
||||
expectedSpec := createVirtualServiceSpec(ch, svcName)
|
||||
|
||||
vs := istio.VirtualService{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "cm-acme-http-solver-",
|
||||
Namespace: ch.Namespace,
|
||||
Labels: podLabels(ch),
|
||||
OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(ch, challengeGvk)},
|
||||
},
|
||||
Spec: *expectedSpec,
|
||||
}
|
||||
|
||||
unstr, err := vs.ToUnstructured()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val, err := s.DynamicClient.Resource(istio.VirtualServiceGvr()).Namespace(ch.Namespace).Create(ctx, unstr, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
virtualService, err := istio.VirtualServiceFromUnstructured(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return virtualService, nil
|
||||
}
|
||||
|
||||
func (s *Solver) checkAndUpdateVirtualService(ctx context.Context, ch *cmacme.Challenge, svcName string, virtualservice *istio.VirtualService) (*istio.VirtualService, error) {
|
||||
log := logf.FromContext(ctx, "checkAndUpdateVirtualService")
|
||||
|
||||
expectedSpec := createVirtualServiceSpec(ch, svcName)
|
||||
|
||||
spec := &virtualservice.Spec
|
||||
if reflect.DeepEqual(spec, expectedSpec) {
|
||||
return virtualservice, nil
|
||||
}
|
||||
|
||||
log.Info("updating VirtualService")
|
||||
|
||||
virtualservice.Spec = *expectedSpec
|
||||
unstr, err := virtualservice.ToUnstructured()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val, err := s.DynamicClient.Resource(istio.VirtualServiceGvr()).Namespace(ch.Namespace).Update(ctx, unstr, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
virtualService, err := istio.VirtualServiceFromUnstructured(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return virtualService, nil
|
||||
}
|
||||
|
||||
func createVirtualServiceSpec(ch *cmacme.Challenge, svcName string) *networkingv1beta1.VirtualService {
|
||||
http01Istio := ch.Spec.Solver.HTTP01.Istio
|
||||
|
||||
return &networkingv1beta1.VirtualService{
|
||||
ExportTo: []string{"*"},
|
||||
Hosts: []string{ch.Spec.DNSName},
|
||||
Gateways: []string{http01Istio.GatewayNamespace + "/" + http01Istio.GatewayName},
|
||||
Http: []*networkingv1beta1.HTTPRoute{
|
||||
{
|
||||
Match: []*networkingv1beta1.HTTPMatchRequest{
|
||||
{Uri: &networkingv1beta1.StringMatch{MatchType: &networkingv1beta1.StringMatch_Exact{Exact: solverPathFn(ch.Spec.Token)}}},
|
||||
},
|
||||
Route: []*networkingv1beta1.HTTPRouteDestination{
|
||||
{
|
||||
Destination: &networkingv1beta1.Destination{
|
||||
Host: svcName,
|
||||
Port: &networkingv1beta1.PortSelector{Number: acmeSolverListenPort},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1"
|
||||
"github.com/jetstack/cert-manager/pkg/issuer/acme/http/internal/istio"
|
||||
)
|
||||
|
||||
func TestEnsureIstio(t *testing.T) {
|
||||
const svcName = "fakeservice"
|
||||
|
||||
const virtualServiceSpecKey = "virtualservicespec"
|
||||
|
||||
virtualServiceGvr := istio.VirtualServiceGvr()
|
||||
|
||||
testChallenge := cmacme.Challenge{
|
||||
Spec: cmacme.ChallengeSpec{
|
||||
DNSName: "example.com",
|
||||
Solver: cmacme.ACMEChallengeSolver{
|
||||
HTTP01: &cmacme.ACMEChallengeSolverHTTP01{
|
||||
Istio: &cmacme.ACMEChallengeSolverHTTP01Istio{
|
||||
GatewayNamespace: defaultTestNamespace,
|
||||
GatewayName: "test-gateway",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := map[string]solverFixture{
|
||||
"should create VirtualService": {
|
||||
Challenge: &testChallenge,
|
||||
CheckFn: func(t *testing.T, s *solverFixture, args ...interface{}) {
|
||||
vss, err := s.Solver.virtualServiceLister.List(labels.NewSelector())
|
||||
if err != nil {
|
||||
t.Errorf("error listing VirtualServices: %v", err)
|
||||
return
|
||||
}
|
||||
if len(vss) != 1 {
|
||||
t.Errorf("expected one VirtualService to be created, but %d VirtualServices were found", len(vss))
|
||||
}
|
||||
},
|
||||
},
|
||||
"should not modify correct VirtualService": {
|
||||
Challenge: &testChallenge,
|
||||
PreFn: func(t *testing.T, s *solverFixture) {
|
||||
virtualServiceSpec := createVirtualServiceSpec(&testChallenge, svcName)
|
||||
s.testResources[virtualServiceSpecKey] = virtualServiceSpec
|
||||
virtualService := istio.VirtualService{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "test-gateway-",
|
||||
Namespace: testChallenge.Namespace,
|
||||
Labels: podLabels(&testChallenge),
|
||||
OwnerReferences: []metav1.OwnerReference{},
|
||||
},
|
||||
Spec: *virtualServiceSpec,
|
||||
}
|
||||
unstr, err := virtualService.ToUnstructured()
|
||||
if err != nil {
|
||||
t.Errorf("error converting to unstructured: %v", err)
|
||||
}
|
||||
_, err = s.FakeDynamicClient().Resource(virtualServiceGvr).Namespace(testChallenge.Namespace).Create(context.Background(), unstr, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("error preparing test: %v", err)
|
||||
}
|
||||
},
|
||||
CheckFn: func(t *testing.T, s *solverFixture, args ...interface{}) {
|
||||
vss, err := s.Solver.virtualServiceLister.List(labels.NewSelector())
|
||||
if err != nil {
|
||||
t.Errorf("error listing VirtualServices: %v", err)
|
||||
return
|
||||
}
|
||||
if len(vss) != 1 {
|
||||
t.Errorf("expected one VirtualService to be created, but %d VirtualServices were found", len(vss))
|
||||
return
|
||||
}
|
||||
newVirtualService, err := istio.VirtualServiceFromUnstructured(vss[0])
|
||||
if err != nil {
|
||||
t.Errorf("could not decode retrieved VirtualService: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
oldVirtualServiceSpec := s.testResources[virtualServiceSpecKey]
|
||||
newVirtualServiceSpec := &newVirtualService.Spec
|
||||
if reflect.TypeOf(oldVirtualServiceSpec) != reflect.TypeOf(newVirtualServiceSpec) {
|
||||
t.Errorf("types should be equal (error in test)")
|
||||
}
|
||||
if !reflect.DeepEqual(oldVirtualServiceSpec, newVirtualServiceSpec) {
|
||||
t.Errorf("did not expect correct virtualservice to be modified")
|
||||
}
|
||||
},
|
||||
},
|
||||
"should fix existing VirtualService": {
|
||||
Challenge: &testChallenge,
|
||||
PreFn: func(t *testing.T, s *solverFixture) {
|
||||
virtualServiceSpec := createVirtualServiceSpec(&testChallenge, svcName+"-needs-fixing")
|
||||
s.testResources[virtualServiceSpecKey] = virtualServiceSpec
|
||||
virtualService := istio.VirtualService{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "test-gateway-",
|
||||
Namespace: testChallenge.Namespace,
|
||||
Labels: podLabels(&testChallenge),
|
||||
OwnerReferences: []metav1.OwnerReference{},
|
||||
},
|
||||
Spec: *virtualServiceSpec,
|
||||
}
|
||||
unstr, err := virtualService.ToUnstructured()
|
||||
if err != nil {
|
||||
t.Errorf("error converting to unstructured: %v", err)
|
||||
}
|
||||
_, err = s.FakeDynamicClient().Resource(virtualServiceGvr).Namespace(testChallenge.Namespace).Create(context.Background(), unstr, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("error preparing test: %v", err)
|
||||
}
|
||||
},
|
||||
CheckFn: func(t *testing.T, s *solverFixture, args ...interface{}) {
|
||||
vss, err := s.Solver.virtualServiceLister.List(labels.NewSelector())
|
||||
if err != nil {
|
||||
t.Errorf("error listing VirtualServices: %v", err)
|
||||
return
|
||||
}
|
||||
if len(vss) != 1 {
|
||||
t.Errorf("expected one VirtualService to be created, but %d VirtualServices were found", len(vss))
|
||||
return
|
||||
}
|
||||
newVirtualService, err := istio.VirtualServiceFromUnstructured(vss[0])
|
||||
if err != nil {
|
||||
t.Errorf("could not decode retrieved VirtualService: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
oldVirtualServiceSpec := s.testResources[virtualServiceSpecKey]
|
||||
newVirtualServiceSpec := &newVirtualService.Spec
|
||||
if reflect.TypeOf(oldVirtualServiceSpec) != reflect.TypeOf(newVirtualServiceSpec) {
|
||||
t.Errorf("types should be equal (error in test)")
|
||||
}
|
||||
if reflect.DeepEqual(oldVirtualServiceSpec, newVirtualServiceSpec) {
|
||||
t.Errorf("expected existing VirtualService spec to be fixed")
|
||||
}
|
||||
if newVirtualServiceSpec.Http[0].Route[0].Destination.Host != svcName {
|
||||
t.Errorf("expected virtualservice destination service to be fixed")
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
test.Setup(t)
|
||||
resp, err := test.Solver.ensureIstio(context.TODO(), test.Challenge, svcName)
|
||||
if err != nil && !test.Err {
|
||||
t.Errorf("Expected function to not error, but got: %v", err)
|
||||
}
|
||||
if err == nil && test.Err {
|
||||
t.Errorf("Expected function to get an error, but got: %v", err)
|
||||
}
|
||||
test.Finish(t, resp, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -144,10 +144,12 @@ func (s *Solver) buildPod(ch *cmacme.Challenge) *corev1.Pod {
|
||||
pod := s.buildDefaultPod(ch)
|
||||
|
||||
// Override defaults if they have changed in the pod template.
|
||||
if ch.Spec.Solver.HTTP01 != nil &&
|
||||
ch.Spec.Solver.HTTP01.Ingress != nil {
|
||||
pod = s.mergePodObjectMetaWithPodTemplate(pod,
|
||||
ch.Spec.Solver.HTTP01.Ingress.PodTemplate)
|
||||
if ch.Spec.Solver.HTTP01 != nil {
|
||||
if ch.Spec.Solver.HTTP01.Ingress != nil {
|
||||
pod = s.mergePodObjectMetaWithPodTemplate(pod, ch.Spec.Solver.HTTP01.Ingress.PodTemplate)
|
||||
} else if ch.Spec.Solver.HTTP01.Istio != nil {
|
||||
pod = s.mergePodObjectMetaWithPodTemplate(pod, ch.Spec.Solver.HTTP01.Istio.PodTemplate)
|
||||
}
|
||||
}
|
||||
|
||||
return pod
|
||||
|
||||
@@ -125,12 +125,12 @@ func buildService(ch *cmacme.Challenge) (*corev1.Service, error) {
|
||||
}
|
||||
|
||||
// checking for presence of http01 config and if set serviceType is set, override our default (NodePort)
|
||||
httpDomainCfg, err := httpDomainCfgForChallenge(ch)
|
||||
serviceType, err := serviceTypeForChallenge(ch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if httpDomainCfg.ServiceType != "" {
|
||||
service.Spec.Type = httpDomainCfg.ServiceType
|
||||
if serviceType != "" {
|
||||
service.Spec.Type = serviceType
|
||||
}
|
||||
|
||||
return service, nil
|
||||
|
||||
@@ -11,6 +11,7 @@ go_library(
|
||||
"ginkgo.go",
|
||||
"helm.go",
|
||||
"ingress_controller.go",
|
||||
"istio.go",
|
||||
"samplewebhook.go",
|
||||
"suite.go",
|
||||
"tiller.go",
|
||||
|
||||
@@ -24,12 +24,14 @@ type ACMEServer struct {
|
||||
URL string
|
||||
DNSServer string
|
||||
IngressIP string
|
||||
IstioIP string
|
||||
}
|
||||
|
||||
func (p *ACMEServer) AddFlags(fs *flag.FlagSet) {
|
||||
fs.StringVar(&p.URL, "acme-server-url", "https://pebble.pebble.svc.cluster.local/dir", "URL for the ACME server used during end-to-end tests")
|
||||
fs.StringVar(&p.DNSServer, "acme-dns-server", "10.0.0.16", "DNS server for ACME DNS01 tests to run against using RFC2136")
|
||||
fs.StringVar(&p.IngressIP, "acme-ingress-ip", "10.0.0.15", "IP of the ingress server that solves HTTP01 ACME challenges")
|
||||
fs.StringVar(&p.IngressIP, "acme-ingress-ip", "10.0.0.15", "IP of the ingress server that solves HTTP01 ACME Ingress challenges")
|
||||
fs.StringVar(&p.IstioIP, "acme-istio-ip", "10.0.0.14", "IP of the ingress server that solves HTTP01 ACME Istio challenges")
|
||||
}
|
||||
|
||||
func (p *ACMEServer) Validate() []error {
|
||||
|
||||
@@ -33,9 +33,13 @@ type Addons struct {
|
||||
ACMEServer ACMEServer
|
||||
|
||||
// IngressController contains configuration for the ingress controller
|
||||
// being used during ACME HTTP01 tests.
|
||||
// being used during ACME HTTP01 Ingress tests.
|
||||
IngressController IngressController
|
||||
|
||||
// Istio contains configuration for the istio virtualservice controller
|
||||
// being used during ACME HTTP01 Istio tests.
|
||||
Istio Istio
|
||||
|
||||
// Venafi describes global configuration variables for the Venafi tests.
|
||||
// This includes credentials for the Venafi TPP server to use during runs.
|
||||
Venafi Venafi
|
||||
@@ -52,6 +56,7 @@ func (a *Addons) AddFlags(fs *flag.FlagSet) {
|
||||
a.Helm.AddFlags(fs)
|
||||
a.ACMEServer.AddFlags(fs)
|
||||
a.IngressController.AddFlags(fs)
|
||||
a.Istio.AddFlags(fs)
|
||||
a.Venafi.AddFlags(fs)
|
||||
a.CertManager.AddFlags(fs)
|
||||
a.DNS01Webhook.AddFlags(fs)
|
||||
@@ -63,6 +68,7 @@ func (c *Addons) Validate() []error {
|
||||
errs = append(errs, c.Helm.Validate()...)
|
||||
errs = append(errs, c.ACMEServer.Validate()...)
|
||||
errs = append(errs, c.IngressController.Validate()...)
|
||||
errs = append(errs, c.Istio.Validate()...)
|
||||
errs = append(errs, c.Venafi.Validate()...)
|
||||
errs = append(errs, c.CertManager.Validate()...)
|
||||
errs = append(errs, c.DNS01Webhook.Validate()...)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
)
|
||||
|
||||
type Istio struct {
|
||||
// Domain is a domain name that can be used during e2e tests.
|
||||
// This domain should have records for *.example.com and example.com pointing
|
||||
// to the IP of the Istio ingress gateway's Service resource.
|
||||
Domain string
|
||||
|
||||
// GatewayNamespace is the namespace of the gateway resource used for the HTTP01 ACME validation tests.
|
||||
GatewayNamespace string
|
||||
|
||||
// GatewayName is the name of the gateway resource used for the HTTP01 ACME validation tests.
|
||||
GatewayName string
|
||||
}
|
||||
|
||||
func (n *Istio) AddFlags(fs *flag.FlagSet) {
|
||||
fs.StringVar(&n.Domain, "istio-ingress-domain", "istio.http01.example.com", "The domain name used during ACME DNS01 validation tests. "+
|
||||
"All subdomains of this domain must also resolve to the IP of the Istio ingress gateway's Service.")
|
||||
fs.StringVar(&n.GatewayNamespace, "istio-gateway-namespace", "istio-system", "The namespace of the gateway resource used for the HTTP01 ACME validation tests")
|
||||
fs.StringVar(&n.GatewayName, "istio-gateway-name", "ingress", "The name of the gateway resource used for the HTTP01 ACME validation tests")
|
||||
}
|
||||
|
||||
func (n *Istio) Validate() []error {
|
||||
return nil
|
||||
}
|
||||
@@ -18,6 +18,8 @@ package helper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -57,3 +59,27 @@ func (h *Helper) WaitForSecretCertificateData(ns, name string, timeout time.Dura
|
||||
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
// GetSecretDNSNames decodes and returns the dns names (SANs) contained in a
|
||||
// certificate secret.
|
||||
func (h *Helper) GetSecretDNSNames(s *corev1.Secret) ([]string, error) {
|
||||
if s.Data == nil {
|
||||
return nil, fmt.Errorf("secret contains no data")
|
||||
}
|
||||
pkData := s.Data[corev1.TLSPrivateKeyKey]
|
||||
certData := s.Data[corev1.TLSCertKey]
|
||||
if len(pkData) == 0 || len(certData) == 0 {
|
||||
return nil, fmt.Errorf("missing data in CA secret")
|
||||
}
|
||||
cert, err := tls.X509KeyPair(certData, pkData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse data in CA secret: %w", err)
|
||||
}
|
||||
|
||||
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("internal error parsing x509 certificate: %w", err)
|
||||
}
|
||||
|
||||
return x509Cert.DNSNames, nil
|
||||
}
|
||||
|
||||
@@ -79,11 +79,20 @@ func runACMEIssuerTests(eab *cmacme.ACMEExternalAccountBinding) {
|
||||
|
||||
(&certificates.Suite{
|
||||
Name: "ACME HTTP01 Issuer",
|
||||
DomainSuffixType: certificates.IngressDomainSuffixType,
|
||||
CreateIssuerFunc: provisionerHTTP01.createHTTP01Issuer,
|
||||
DeleteIssuerFunc: provisionerHTTP01.delete,
|
||||
UnsupportedFeatures: unsupportedHTTP01Features,
|
||||
}).Define()
|
||||
|
||||
(&certificates.Suite{
|
||||
Name: "ACME HTTP01 Issuer on Istio",
|
||||
DomainSuffixType: certificates.IstioDomainSuffixType,
|
||||
CreateIssuerFunc: provisionerHTTP01.createHTTP01IssuerOnIstio,
|
||||
DeleteIssuerFunc: provisionerHTTP01.delete,
|
||||
UnsupportedFeatures: unsupportedHTTP01Features,
|
||||
}).Define()
|
||||
|
||||
(&certificates.Suite{
|
||||
Name: "ACME DNS01 Issuer",
|
||||
DomainSuffix: "dns01.example.com",
|
||||
@@ -94,6 +103,7 @@ func runACMEIssuerTests(eab *cmacme.ACMEExternalAccountBinding) {
|
||||
|
||||
(&certificates.Suite{
|
||||
Name: "ACME HTTP01 ClusterIssuer",
|
||||
DomainSuffixType: certificates.IngressDomainSuffixType,
|
||||
CreateIssuerFunc: provisionerHTTP01.createHTTP01ClusterIssuer,
|
||||
DeleteIssuerFunc: provisionerHTTP01.delete,
|
||||
UnsupportedFeatures: unsupportedHTTP01Features,
|
||||
@@ -152,6 +162,27 @@ func (a *acmeIssuerProvisioner) createHTTP01Issuer(f *framework.Framework) cmmet
|
||||
}
|
||||
}
|
||||
|
||||
func (a *acmeIssuerProvisioner) createHTTP01IssuerOnIstio(f *framework.Framework) cmmeta.ObjectReference {
|
||||
a.ensureEABSecret(f, "")
|
||||
|
||||
By("Creating an ACME HTTP01 Istio Issuer")
|
||||
issuer := &cmapi.Issuer{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "acme-issuer-http01-",
|
||||
},
|
||||
Spec: a.createHTTP01IssuerOnIstioSpec(f.Config.Addons.ACMEServer.URL),
|
||||
}
|
||||
|
||||
issuer, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), issuer, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to create acme HTTP01 Istio issuer")
|
||||
|
||||
return cmmeta.ObjectReference{
|
||||
Group: cmapi.SchemeGroupVersion.Group,
|
||||
Kind: cmapi.IssuerKind,
|
||||
Name: issuer.Name,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *acmeIssuerProvisioner) createHTTP01ClusterIssuer(f *framework.Framework) cmmeta.ObjectReference {
|
||||
a.ensureEABSecret(f, f.Config.Addons.CertManager.ClusterResourceNamespace)
|
||||
|
||||
@@ -200,6 +231,36 @@ func (a *acmeIssuerProvisioner) createHTTP01IssuerSpec(serverURL string) cmapi.I
|
||||
}
|
||||
}
|
||||
|
||||
func (a *acmeIssuerProvisioner) createHTTP01IssuerOnIstioSpec(serverURL string) cmapi.IssuerSpec {
|
||||
const TestGatewayNamespace = "istio-system"
|
||||
const TestGatewayName = "ingress"
|
||||
|
||||
return cmapi.IssuerSpec{
|
||||
IssuerConfig: cmapi.IssuerConfig{
|
||||
ACME: &cmacme.ACMEIssuer{
|
||||
Server: serverURL,
|
||||
SkipTLSVerify: true,
|
||||
PrivateKey: cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "acme-private-key-http01",
|
||||
},
|
||||
},
|
||||
ExternalAccountBinding: a.eab,
|
||||
Solvers: []cmacme.ACMEChallengeSolver{
|
||||
{
|
||||
HTTP01: &cmacme.ACMEChallengeSolverHTTP01{
|
||||
Istio: &cmacme.ACMEChallengeSolverHTTP01Istio{
|
||||
GatewayNamespace: TestGatewayNamespace,
|
||||
GatewayName: TestGatewayName,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (a *acmeIssuerProvisioner) createDNS01Issuer(f *framework.Framework) cmmeta.ObjectReference {
|
||||
a.ensureEABSecret(f, f.Namespace.Name)
|
||||
|
||||
|
||||
@@ -28,6 +28,11 @@ import (
|
||||
"github.com/jetstack/cert-manager/test/e2e/framework/helper/featureset"
|
||||
)
|
||||
|
||||
const (
|
||||
IstioDomainSuffixType = "istio"
|
||||
IngressDomainSuffixType = "ingress"
|
||||
)
|
||||
|
||||
// Suite defines a reusable conformance test suite that can be used against any
|
||||
// Issuer implementation.
|
||||
type Suite struct {
|
||||
@@ -56,6 +61,10 @@ type Suite struct {
|
||||
// nginx-ingress addon.
|
||||
DomainSuffix string
|
||||
|
||||
// DomainSuffixType is a string used to detect what DomainSuffix to use
|
||||
// in case DomainSuffix is left empty.
|
||||
DomainSuffixType string
|
||||
|
||||
// UnsupportedFeatures is a list of features that are not supported by this
|
||||
// invocation of the test suite.
|
||||
// This is useful if a particular issuers explicitly does not support
|
||||
@@ -68,12 +77,23 @@ type Suite struct {
|
||||
|
||||
// complete will validate configuration and set default values.
|
||||
func (s *Suite) complete(f *framework.Framework) {
|
||||
// TODO: work out how to fail an entire 'Describe' block so we can validate these are correctly set
|
||||
//Expect(s.Name).NotTo(Equal(""), "Name must be set")
|
||||
//Expect(s.CreateIssuerFunc).NotTo(BeNil(), "CreateIssuerFunc must be set")
|
||||
if s.Name == "" {
|
||||
Fail("Name must be set")
|
||||
}
|
||||
|
||||
if s.CreateIssuerFunc == nil {
|
||||
Fail("CreateIssuerFunc must be set")
|
||||
}
|
||||
|
||||
if s.DomainSuffix == "" {
|
||||
s.DomainSuffix = f.Config.Addons.IngressController.Domain
|
||||
switch s.DomainSuffixType {
|
||||
case IngressDomainSuffixType, "":
|
||||
s.DomainSuffix = f.Config.Addons.IngressController.Domain
|
||||
case IstioDomainSuffixType:
|
||||
s.DomainSuffix = f.Config.Addons.Istio.Domain
|
||||
default:
|
||||
Fail("Domain suffix type not recognised")
|
||||
}
|
||||
}
|
||||
|
||||
if s.UnsupportedFeatures == nil {
|
||||
@@ -138,3 +158,7 @@ func (s *Suite) newDomainDepth(depth int) string {
|
||||
}
|
||||
return strings.Join(append(subdomains, s.DomainSuffix), ".")
|
||||
}
|
||||
|
||||
func (s *Suite) newDomainLength(length int) string {
|
||||
return fmt.Sprintf("%s.%s", util.RandStringRunes(length), s.DomainSuffix)
|
||||
}
|
||||
|
||||
@@ -574,5 +574,86 @@ func (s *Suite) Define() {
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certName, f.Helper().ValidationSetForUnsupportedFeatureSet(s.UnsupportedFeatures)...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
s.it(f, "should allow updating an existing certificate with a new dns name", func(issuerRef cmmeta.ObjectReference) {
|
||||
testCertificate := &cmapi.Certificate{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "testcert",
|
||||
Namespace: f.Namespace.Name,
|
||||
},
|
||||
Spec: cmapi.CertificateSpec{
|
||||
SecretName: "testcert-tls",
|
||||
DNSNames: []string{s.newDomain()},
|
||||
IssuerRef: issuerRef,
|
||||
},
|
||||
}
|
||||
validations := f.Helper().ValidationSetForUnsupportedFeatureSet(s.UnsupportedFeatures)
|
||||
|
||||
By("Creating a Certificate")
|
||||
err := f.CRClient.Create(ctx, testCertificate)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, "testcert", time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, "testcert", validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Getting the latest version of the Certificate")
|
||||
cert, err := f.Helper().CMClient.CertmanagerV1().Certificates(f.Namespace.Name).Get(context.TODO(), "testcert", metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Adding an additional dnsName to the Certificate")
|
||||
newDNSName := s.newDomain()
|
||||
cert.Spec.DNSNames = append(cert.Spec.DNSNames, newDNSName)
|
||||
|
||||
By("Updating the Certificate in the apiserver")
|
||||
err = f.CRClient.Update(context.TODO(), cert)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be not ready")
|
||||
_, err = f.Helper().WaitForCertificateNotReady(f.Namespace.Name, "testcert", time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, "testcert", time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, "testcert", validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}, featureset.OnlySAN)
|
||||
|
||||
s.it(f, "should obtain a signed certificate for a long domain", func(issuerRef cmmeta.ObjectReference) {
|
||||
// the maximum length of a single segment of the domain being requested
|
||||
const maxLengthOfDomainSegment = 63
|
||||
|
||||
testCertificate := &cmapi.Certificate{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "testcert",
|
||||
Namespace: f.Namespace.Name,
|
||||
},
|
||||
Spec: cmapi.CertificateSpec{
|
||||
SecretName: "testcert-tls",
|
||||
DNSNames: []string{s.newDomainLength(maxLengthOfDomainSegment)},
|
||||
IssuerRef: issuerRef,
|
||||
},
|
||||
}
|
||||
validations := f.Helper().ValidationSetForUnsupportedFeatureSet(s.UnsupportedFeatures)
|
||||
|
||||
By("Creating a Certificate")
|
||||
err := f.CRClient.Create(ctx, testCertificate)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, "testcert", time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, "testcert", validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}, featureset.OnlySAN)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"dns01.go",
|
||||
"http01.go",
|
||||
"http01_ingress.go",
|
||||
"http01_istio.go",
|
||||
"notafter.go",
|
||||
"webhook.go",
|
||||
],
|
||||
|
||||
+4
-158
@@ -18,8 +18,6 @@ package certificate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -49,9 +47,8 @@ const testingACMEEmail = "e2e@cert-manager.io"
|
||||
const testingACMEPrivateKey = "test-acme-private-key"
|
||||
const foreverTestTimeout = time.Second * 60
|
||||
|
||||
var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
|
||||
f := framework.NewDefaultFramework("create-acme-certificate-http01")
|
||||
h := f.Helper()
|
||||
var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01) Ingress", func() {
|
||||
f := framework.NewDefaultFramework("create-acme-certificate-http01-ingress")
|
||||
|
||||
var acmeIngressDomain string
|
||||
issuerName := "test-acme-issuer"
|
||||
@@ -183,33 +180,6 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should obtain a signed certificate for a long domain using http01 validation", func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)
|
||||
|
||||
// the maximum length of a single segment of the domain being requested
|
||||
const maxLengthOfDomainSegment = 63
|
||||
By("Creating a Certificate")
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert := gen.Certificate(certificateName,
|
||||
gen.SetCertificateSecretName(certificateSecretName),
|
||||
gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
|
||||
gen.SetCertificateDNSNames(acmeIngressDomain, fmt.Sprintf("%s.%s", cmutil.RandStringRunes(maxLengthOfDomainSegment), acmeIngressDomain)),
|
||||
)
|
||||
cert.Namespace = f.Namespace.Name
|
||||
|
||||
_, err := certClient.Create(context.TODO(), cert, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should obtain a signed certificate with a CN and single subdomain as dns name from the ACME server", func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)
|
||||
|
||||
@@ -234,57 +204,6 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should allow updating an existing certificate with a new dns name", func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert := gen.Certificate(certificateName,
|
||||
gen.SetCertificateSecretName(certificateSecretName),
|
||||
gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
|
||||
gen.SetCertificateDNSNames(fmt.Sprintf("%s.%s", cmutil.RandStringRunes(5), acmeIngressDomain)),
|
||||
)
|
||||
cert.Namespace = f.Namespace.Name
|
||||
|
||||
_, err := certClient.Create(context.TODO(), cert, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying the Certificate is valid")
|
||||
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Getting the latest version of the Certificate")
|
||||
cert, err = certClient.Get(context.TODO(), certificateName, metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Adding an additional dnsName to the Certificate")
|
||||
newDNSName := fmt.Sprintf("%s.%s", cmutil.RandStringRunes(5), acmeIngressDomain)
|
||||
cert.Spec.DNSNames = append(cert.Spec.DNSNames, newDNSName)
|
||||
|
||||
By("Updating the Certificate in the apiserver")
|
||||
cert, err = certClient.Update(context.TODO(), cert, metav1.UpdateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be not ready")
|
||||
_, err = h.WaitForCertificateNotReady(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to become ready & valid")
|
||||
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should allow updating the dns name of a failing certificate that had an incorrect dns name", func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)
|
||||
|
||||
@@ -322,7 +241,7 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be not ready")
|
||||
_, err = h.WaitForCertificateNotReady(f.Namespace.Name, certificateName, 30*time.Second)
|
||||
_, err = f.Helper().WaitForCertificateNotReady(f.Namespace.Name, certificateName, 30*time.Second)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Getting the latest version of the Certificate")
|
||||
@@ -344,7 +263,7 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
|
||||
|
||||
By("Checking that the secret contains this dns name")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, func(cert *v1.Certificate, secret *corev1.Secret) error {
|
||||
dnsnames, err := findDNSNames(secret)
|
||||
dnsnames, err := f.Helper().GetSecretDNSNames(secret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -399,7 +318,6 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
|
||||
})
|
||||
|
||||
It("should obtain a signed certificate with a single CN from the ACME server when redirected", func() {
|
||||
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)
|
||||
|
||||
// force-ssl-redirect should make every request turn into a redirect,
|
||||
@@ -602,76 +520,4 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should allow updating an existing certificate with a new dns name", func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert := gen.Certificate(certificateName,
|
||||
gen.SetCertificateSecretName(certificateSecretName),
|
||||
gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
|
||||
gen.SetCertificateDNSNames(fmt.Sprintf("%s.%s", cmutil.RandStringRunes(5), acmeIngressDomain)),
|
||||
)
|
||||
cert.Namespace = f.Namespace.Name
|
||||
|
||||
_, err := certClient.Create(context.TODO(), cert, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Getting the latest version of the Certificate")
|
||||
cert, err = certClient.Get(context.TODO(), certificateName, metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Adding an additional dnsName to the Certificate")
|
||||
newDNSName := fmt.Sprintf("%s.%s", cmutil.RandStringRunes(5), acmeIngressDomain)
|
||||
cert.Spec.DNSNames = append(cert.Spec.DNSNames, newDNSName)
|
||||
|
||||
By("Updating the Certificate in the apiserver")
|
||||
cert, err = certClient.Update(context.TODO(), cert, metav1.UpdateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be not ready")
|
||||
_, err = h.WaitForCertificateNotReady(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
// findDNSNames decodes and returns the dns names (SANs) contained in a
|
||||
// certificate secret.
|
||||
func findDNSNames(s *corev1.Secret) ([]string, error) {
|
||||
if s.Data == nil {
|
||||
return nil, fmt.Errorf("secret contains no data")
|
||||
}
|
||||
pkData := s.Data[corev1.TLSPrivateKeyKey]
|
||||
certData := s.Data[corev1.TLSCertKey]
|
||||
if len(pkData) == 0 || len(certData) == 0 {
|
||||
return nil, fmt.Errorf("missing data in CA secret")
|
||||
}
|
||||
cert, err := tls.X509KeyPair(certData, pkData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse data in CA secret: %w", err)
|
||||
}
|
||||
|
||||
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("internal error parsing x509 certificate: %w", err)
|
||||
}
|
||||
|
||||
return x509Cert.DNSNames, nil
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1"
|
||||
v1 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
|
||||
cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
|
||||
cmutil "github.com/jetstack/cert-manager/pkg/util"
|
||||
"github.com/jetstack/cert-manager/test/e2e/framework"
|
||||
"github.com/jetstack/cert-manager/test/e2e/framework/helper/featureset"
|
||||
"github.com/jetstack/cert-manager/test/e2e/framework/log"
|
||||
. "github.com/jetstack/cert-manager/test/e2e/framework/matcher"
|
||||
frameworkutil "github.com/jetstack/cert-manager/test/e2e/framework/util"
|
||||
"github.com/jetstack/cert-manager/test/e2e/util"
|
||||
"github.com/jetstack/cert-manager/test/unit/gen"
|
||||
)
|
||||
|
||||
const istioTestingACMEEmail = "e2e@cert-manager.io"
|
||||
const istioTestingACMEPrivateKey = "test-acme-private-key"
|
||||
const istioForeverTestTimeout = time.Second * 60
|
||||
|
||||
var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01) Istio", func() {
|
||||
f := framework.NewDefaultFramework("create-acme-certificate-http01-istio")
|
||||
|
||||
var acmeIngressDomain string
|
||||
issuerName := "test-acme-issuer"
|
||||
certificateName := "test-acme-certificate"
|
||||
certificateSecretName := "test-acme-certificate"
|
||||
|
||||
// ACME Issuer does not return a ca.crt. See:
|
||||
// https://github.com/jetstack/cert-manager/issues/1571
|
||||
unsupportedFeatures := featureset.NewFeatureSet(featureset.SaveCAToSecret)
|
||||
validations := f.Helper().ValidationSetForUnsupportedFeatureSet(unsupportedFeatures)
|
||||
|
||||
BeforeEach(func() {
|
||||
solvers := []cmacme.ACMEChallengeSolver{
|
||||
{
|
||||
HTTP01: &cmacme.ACMEChallengeSolverHTTP01{
|
||||
Istio: &cmacme.ACMEChallengeSolverHTTP01Istio{
|
||||
GatewayNamespace: f.Config.Addons.Istio.GatewayNamespace,
|
||||
GatewayName: f.Config.Addons.Istio.GatewayName,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
acmeIssuer := gen.Issuer(issuerName,
|
||||
gen.SetIssuerNamespace(f.Namespace.Name),
|
||||
gen.SetIssuerACMEEmail(istioTestingACMEEmail),
|
||||
gen.SetIssuerACMEURL(f.Config.Addons.ACMEServer.URL),
|
||||
gen.SetIssuerACMEPrivKeyRef(istioTestingACMEPrivateKey),
|
||||
gen.SetIssuerACMESkipTLSVerify(true),
|
||||
gen.SetIssuerACMESolvers(solvers))
|
||||
By("Creating an Issuer")
|
||||
_, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), acmeIssuer, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Waiting for Issuer to become Ready")
|
||||
err = util.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
|
||||
issuerName,
|
||||
v1.IssuerCondition{
|
||||
Type: v1.IssuerConditionReady,
|
||||
Status: cmmeta.ConditionTrue,
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying the ACME account URI is set")
|
||||
err = util.WaitForIssuerStatusFunc(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
|
||||
issuerName,
|
||||
func(i *v1.Issuer) (bool, error) {
|
||||
if i.GetStatus().ACMEStatus().URI == "" {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying ACME account private key exists")
|
||||
secret, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(context.TODO(), istioTestingACMEPrivateKey, metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if len(secret.Data) != 1 {
|
||||
Fail("Expected 1 key in ACME account private key secret, but there was %d", len(secret.Data))
|
||||
}
|
||||
})
|
||||
|
||||
JustBeforeEach(func() {
|
||||
acmeIngressDomain = frameworkutil.RandomSubdomain(f.Config.Addons.Istio.Domain)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
By("Cleaning up")
|
||||
f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Delete(context.TODO(), issuerName, metav1.DeleteOptions{})
|
||||
f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(context.TODO(), istioTestingACMEPrivateKey, metav1.DeleteOptions{})
|
||||
})
|
||||
|
||||
It("should obtain a signed certificate with a single CN from the ACME server", func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert := gen.Certificate(certificateName,
|
||||
gen.SetCertificateSecretName(certificateSecretName),
|
||||
gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
|
||||
gen.SetCertificateDNSNames(acmeIngressDomain),
|
||||
)
|
||||
cert.Namespace = f.Namespace.Name
|
||||
|
||||
_, err := certClient.Create(context.TODO(), cert, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should obtain a signed ecdsa certificate with a single CN from the ACME server", func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert := gen.Certificate(certificateName,
|
||||
gen.SetCertificateSecretName(certificateSecretName),
|
||||
gen.SetCertificateIssuer(cmmeta.ObjectReference{
|
||||
Name: issuerName,
|
||||
}),
|
||||
gen.SetCertificateDNSNames(acmeIngressDomain),
|
||||
gen.SetCertificateKeyAlgorithm(v1.ECDSAKeyAlgorithm),
|
||||
)
|
||||
cert.Namespace = f.Namespace.Name
|
||||
_, err := certClient.Create(context.TODO(), cert, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should obtain a signed certificate with a CN and single subdomain as dns name from the ACME server", func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert := gen.Certificate(certificateName,
|
||||
gen.SetCertificateSecretName(certificateSecretName),
|
||||
gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
|
||||
gen.SetCertificateDNSNames(fmt.Sprintf("%s.%s", cmutil.RandStringRunes(5), acmeIngressDomain)),
|
||||
)
|
||||
cert.Namespace = f.Namespace.Name
|
||||
|
||||
_, err := certClient.Create(context.TODO(), cert, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying the Certificate is valid")
|
||||
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should allow updating the dns name of a failing certificate that had an incorrect dns name", func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)
|
||||
|
||||
By("Creating a failing Certificate")
|
||||
cert := gen.Certificate(certificateName,
|
||||
gen.SetCertificateSecretName(certificateSecretName),
|
||||
gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
|
||||
gen.SetCertificateDNSNames("google.com"),
|
||||
)
|
||||
cert.Namespace = f.Namespace.Name
|
||||
|
||||
_, err := certClient.Create(context.TODO(), cert, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Making sure the Order failed with a 400 since google.com is invalid")
|
||||
order := &cmacme.Order{}
|
||||
err = wait.PollImmediate(1*time.Second, 1*time.Minute, func() (done bool, err error) {
|
||||
orders, err := listOwnedOrders(f.CertManagerClientSet, cert)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
if len(orders) == 0 || len(orders) > 1 {
|
||||
log.Logf("Waiting as one Order should exist, but we found %d", len(orders))
|
||||
return false, nil
|
||||
}
|
||||
order = orders[0]
|
||||
|
||||
expected := `400 urn:ietf:params:acme:error:rejectedIdentifier`
|
||||
if !strings.Contains(order.Status.Reason, expected) {
|
||||
log.Logf("Waiting for Order's reason, current: %s, should contain: %s", order.Status.Reason, expected)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be not ready")
|
||||
_, err = f.Helper().WaitForCertificateNotReady(f.Namespace.Name, certificateName, 30*time.Second)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Getting the latest version of the Certificate")
|
||||
cert, err = certClient.Get(context.TODO(), certificateName, metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Replacing dnsNames with a valid dns name")
|
||||
cert.Spec.DNSNames = []string{fmt.Sprintf("%s.%s", cmutil.RandStringRunes(5), acmeIngressDomain)}
|
||||
_, err = certClient.Update(context.TODO(), cert, metav1.UpdateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to have the Ready=True condition")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Sanity checking the issued Certificate")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Checking that the secret contains this dns name")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, func(cert *v1.Certificate, secret *corev1.Secret) error {
|
||||
dnsnames, err := f.Helper().GetSecretDNSNames(secret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Expect(cert.Spec.DNSNames).To(ContainElements(dnsnames))
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should fail to obtain a certificate for an invalid ACME dns name", func() {
|
||||
// create test fixture
|
||||
By("Creating a Certificate")
|
||||
cert := gen.Certificate(certificateName,
|
||||
gen.SetCertificateSecretName(certificateSecretName),
|
||||
gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
|
||||
gen.SetCertificateDNSNames("google.com"),
|
||||
)
|
||||
cert.Namespace = f.Namespace.Name
|
||||
|
||||
cert, err := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name).Create(context.TODO(), cert, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
notReadyCondition := v1.CertificateCondition{
|
||||
Type: v1.CertificateConditionReady,
|
||||
Status: cmmeta.ConditionFalse,
|
||||
}
|
||||
Eventually(cert, "30s", "1s").Should(HaveCondition(f, notReadyCondition))
|
||||
Consistently(cert, "1m", "10s").Should(HaveCondition(f, notReadyCondition))
|
||||
})
|
||||
|
||||
It("should automatically recreate challenge pod and still obtain a certificate if it is manually deleted", func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert := gen.Certificate(certificateName,
|
||||
gen.SetCertificateSecretName(certificateSecretName),
|
||||
gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
|
||||
gen.SetCertificateDNSNames(acmeIngressDomain),
|
||||
)
|
||||
cert.Namespace = f.Namespace.Name
|
||||
_, err := certClient.Create(context.TODO(), cert, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("killing the solver pod")
|
||||
podClient := f.KubeClientSet.CoreV1().Pods(f.Namespace.Name)
|
||||
var pod corev1.Pod
|
||||
err = wait.PollImmediate(1*time.Second, time.Minute,
|
||||
func() (bool, error) {
|
||||
log.Logf("Waiting for solver pod to exist")
|
||||
podlist, err := podClient.List(context.TODO(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, p := range podlist.Items {
|
||||
log.Logf("solver pod %s", p.Name)
|
||||
// TODO(dmo): make this cleaner instead of just going by name
|
||||
if strings.Contains(p.Name, "http-solver") {
|
||||
pod = p
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
|
||||
},
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = podClient.Delete(context.TODO(), pod.Name, metav1.DeleteOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// The pod should get remade and the certificate should be made valid.
|
||||
// Killing the pod could potentially make the validation invalid if pebble
|
||||
// were to ask us for the challenge after the pod was killed, but because
|
||||
// we kill it so early, we should always be in the self-check phase
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should obtain a signed certificate with a single IP Address from the ACME server", func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert := gen.Certificate(certificateName,
|
||||
gen.SetCertificateSecretName(certificateSecretName),
|
||||
gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
|
||||
gen.SetCertificateIPs(f.Config.Addons.ACMEServer.IngressIP),
|
||||
)
|
||||
cert.Namespace = f.Namespace.Name
|
||||
|
||||
_, err := certClient.Create(context.TODO(), cert, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should obtain a signed certificate with an IP and DNS names from the ACME server", func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert := gen.Certificate(certificateName,
|
||||
gen.SetCertificateSecretName(certificateSecretName),
|
||||
gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
|
||||
gen.SetCertificateDNSNames(fmt.Sprintf("%s.%s", cmutil.RandStringRunes(2), acmeIngressDomain)),
|
||||
gen.SetCertificateIPs(f.Config.Addons.ACMEServer.IstioIP),
|
||||
)
|
||||
cert.Namespace = f.Namespace.Name
|
||||
|
||||
_, err := certClient.Create(context.TODO(), cert, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName, validations...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
@@ -4,7 +4,8 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"dns01.go",
|
||||
"http01.go",
|
||||
"http01_ingress.go",
|
||||
"http01_istio.go",
|
||||
],
|
||||
importpath = "github.com/jetstack/cert-manager/test/e2e/suite/issuers/acme/certificaterequest",
|
||||
visibility = ["//visibility:public"],
|
||||
|
||||
+2
-2
@@ -41,8 +41,8 @@ import (
|
||||
"github.com/jetstack/cert-manager/test/unit/gen"
|
||||
)
|
||||
|
||||
var _ = framework.CertManagerDescribe("ACME CertificateRequest (HTTP01)", func() {
|
||||
f := framework.NewDefaultFramework("create-acme-certificate-request-http01")
|
||||
var _ = framework.CertManagerDescribe("ACME CertificateRequest (HTTP01) Ingress", func() {
|
||||
f := framework.NewDefaultFramework("create-acme-certificate-request-http01-ingress")
|
||||
h := f.Helper()
|
||||
|
||||
var acmeIngressDomain string
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1"
|
||||
v1 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
|
||||
cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
|
||||
cmutil "github.com/jetstack/cert-manager/pkg/util"
|
||||
"github.com/jetstack/cert-manager/test/e2e/framework"
|
||||
"github.com/jetstack/cert-manager/test/e2e/framework/log"
|
||||
. "github.com/jetstack/cert-manager/test/e2e/framework/matcher"
|
||||
frameworkutil "github.com/jetstack/cert-manager/test/e2e/framework/util"
|
||||
"github.com/jetstack/cert-manager/test/e2e/util"
|
||||
"github.com/jetstack/cert-manager/test/unit/gen"
|
||||
)
|
||||
|
||||
var _ = framework.CertManagerDescribe("ACME CertificateRequest (HTTP01) Istio", func() {
|
||||
f := framework.NewDefaultFramework("create-acme-certificate-request-http01-istio")
|
||||
h := f.Helper()
|
||||
|
||||
var acmeIngressDomain string
|
||||
issuerName := "test-acme-issuer"
|
||||
certificateRequestName := "test-acme-certificate-request"
|
||||
|
||||
BeforeEach(func() {
|
||||
solvers := []cmacme.ACMEChallengeSolver{
|
||||
{
|
||||
HTTP01: &cmacme.ACMEChallengeSolverHTTP01{
|
||||
Istio: &cmacme.ACMEChallengeSolverHTTP01Istio{
|
||||
GatewayNamespace: f.Config.Addons.Istio.GatewayNamespace,
|
||||
GatewayName: f.Config.Addons.Istio.GatewayName,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
acmeIssuer := gen.Issuer(issuerName,
|
||||
gen.SetIssuerNamespace(f.Namespace.Name),
|
||||
gen.SetIssuerACMEEmail(testingACMEEmail),
|
||||
gen.SetIssuerACMEURL(f.Config.Addons.ACMEServer.URL),
|
||||
gen.SetIssuerACMEPrivKeyRef(testingACMEPrivateKey),
|
||||
gen.SetIssuerACMESkipTLSVerify(true),
|
||||
gen.SetIssuerACMESolvers(solvers))
|
||||
By("Creating an Issuer")
|
||||
_, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), acmeIssuer, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Waiting for Issuer to become Ready")
|
||||
err = util.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
|
||||
issuerName,
|
||||
v1.IssuerCondition{
|
||||
Type: v1.IssuerConditionReady,
|
||||
Status: cmmeta.ConditionTrue,
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying the ACME account URI is set")
|
||||
err = util.WaitForIssuerStatusFunc(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
|
||||
issuerName,
|
||||
func(i *v1.Issuer) (bool, error) {
|
||||
if i.GetStatus().ACMEStatus().URI == "" {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying ACME account private key exists")
|
||||
secret, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(context.TODO(), testingACMEPrivateKey, metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if len(secret.Data) != 1 {
|
||||
Fail("Expected 1 key in ACME account private key secret, but there was %d", len(secret.Data))
|
||||
}
|
||||
})
|
||||
|
||||
JustBeforeEach(func() {
|
||||
acmeIngressDomain = frameworkutil.RandomSubdomain(f.Config.Addons.Istio.Domain)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
By("Cleaning up")
|
||||
f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Delete(context.TODO(), issuerName, metav1.DeleteOptions{})
|
||||
f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(context.TODO(), testingACMEPrivateKey, metav1.DeleteOptions{})
|
||||
})
|
||||
|
||||
It("should obtain a signed certificate with a single CN from the ACME server", func() {
|
||||
crClient := f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name)
|
||||
|
||||
By("Creating a CertificateRequest")
|
||||
cr, key, err := util.NewCertManagerBasicCertificateRequest(certificateRequestName, issuerName, v1.IssuerKind, nil,
|
||||
[]string{acmeIngressDomain}, nil, nil, x509.RSA)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
cr, err = crClient.Create(context.TODO(), cr, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Verifying the Certificate is valid")
|
||||
err = h.WaitCertificateRequestIssuedValid(f.Namespace.Name, certificateRequestName, time.Minute*5, key)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should obtain a signed ecdsa certificate with a single CN from the ACME server", func() {
|
||||
crClient := f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name)
|
||||
|
||||
By("Creating a CertificateRequest")
|
||||
cr, key, err := util.NewCertManagerBasicCertificateRequest(certificateRequestName, issuerName, v1.IssuerKind, nil,
|
||||
[]string{acmeIngressDomain}, nil, nil, x509.ECDSA)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, err = crClient.Create(context.TODO(), cr, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying the Certificate is valid and of type ECDSA")
|
||||
err = h.WaitCertificateRequestIssuedValid(f.Namespace.Name, certificateRequestName, time.Minute*5, key)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should obtain a signed certificate for a long domain using http01 validation", func() {
|
||||
crClient := f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name)
|
||||
|
||||
// the maximum length of a single segment of the domain being requested
|
||||
const maxLengthOfDomainSegment = 63
|
||||
By("Creating a CertificateRequest")
|
||||
cr, key, err := util.NewCertManagerBasicCertificateRequest(certificateRequestName, issuerName, v1.IssuerKind, nil,
|
||||
[]string{acmeIngressDomain, fmt.Sprintf("%s.%s", cmutil.RandStringRunes(maxLengthOfDomainSegment), acmeIngressDomain)},
|
||||
nil, nil, x509.RSA)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, err = crClient.Create(context.TODO(), cr, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = h.WaitCertificateRequestIssuedValid(f.Namespace.Name, certificateRequestName, time.Minute*5, key)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should obtain a signed certificate with a CN and single subdomain as dns name from the ACME server", func() {
|
||||
crClient := f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name)
|
||||
|
||||
By("Creating a CertificateRequest")
|
||||
cr, key, err := util.NewCertManagerBasicCertificateRequest(certificateRequestName, issuerName, v1.IssuerKind, nil,
|
||||
[]string{fmt.Sprintf("%s.%s", cmutil.RandStringRunes(5), acmeIngressDomain)},
|
||||
nil, nil, x509.RSA)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, err = crClient.Create(context.TODO(), cr, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying the CertificateRequest is valid")
|
||||
err = h.WaitCertificateRequestIssuedValid(f.Namespace.Name, certificateRequestName, time.Minute*5, key)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should fail to obtain a certificate for an invalid ACME dns name", func() {
|
||||
// create test fixture
|
||||
By("Creating a CertificateRequest")
|
||||
cr, _, err := util.NewCertManagerBasicCertificateRequest(certificateRequestName, issuerName, v1.IssuerKind, nil,
|
||||
[]string{"google.com"}, nil, nil, x509.RSA)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
cr, err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Create(context.TODO(), cr, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
notReadyCondition := v1.CertificateRequestCondition{
|
||||
Type: v1.CertificateRequestConditionReady,
|
||||
Status: cmmeta.ConditionFalse,
|
||||
}
|
||||
Eventually(cr, "30s", "1s").Should(HaveCondition(f, notReadyCondition))
|
||||
Consistently(cr, "1m", "10s").Should(HaveCondition(f, notReadyCondition))
|
||||
})
|
||||
|
||||
It("should automatically recreate challenge pod and still obtain a certificate if it is manually deleted", func() {
|
||||
crClient := f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name)
|
||||
|
||||
By("Creating a CertificateRequest")
|
||||
cr, key, err := util.NewCertManagerBasicCertificateRequest(certificateRequestName, issuerName, v1.IssuerKind, nil,
|
||||
[]string{acmeIngressDomain}, nil, nil, x509.RSA)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, err = crClient.Create(context.TODO(), cr, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("killing the solver pod")
|
||||
podClient := f.KubeClientSet.CoreV1().Pods(f.Namespace.Name)
|
||||
var pod corev1.Pod
|
||||
err = wait.PollImmediate(1*time.Second, time.Minute,
|
||||
func() (bool, error) {
|
||||
log.Logf("Waiting for solver pod to exist")
|
||||
podlist, err := podClient.List(context.TODO(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, p := range podlist.Items {
|
||||
log.Logf("solver pod %s", p.Name)
|
||||
// TODO(dmo): make this cleaner instead of just going by name
|
||||
if strings.Contains(p.Name, "http-solver") {
|
||||
pod = p
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
|
||||
},
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = podClient.Delete(context.TODO(), pod.Name, metav1.DeleteOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// The pod should get remade and the certificate should be made valid.
|
||||
// Killing the pod could potentially make the validation invalid if pebble
|
||||
// were to ask us for the challenge after the pod was killed, but because
|
||||
// we kill it so early, we should always be in the self-check phase
|
||||
By("Verifying the CertificateRequest is valid")
|
||||
err = h.WaitCertificateRequestIssuedValid(f.Namespace.Name, certificateRequestName, time.Minute*5, key)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user