feat: add NodeGetVolumeStats support
This commit is contained in:
parent
5b2dda0477
commit
94e5bf7020
3
go.sum
3
go.sum
@ -1222,11 +1222,13 @@ k8s.io/apiserver v0.20.0/go.mod h1:6gRIWiOkvGvQt12WTYmsiYoUyYW0FXSiMdNl4m+sxY8=
|
|||||||
k8s.io/cli-runtime v0.20.0/go.mod h1:C5tewU1SC1t09D7pmkk83FT4lMAw+bvMDuRxA7f0t2s=
|
k8s.io/cli-runtime v0.20.0/go.mod h1:C5tewU1SC1t09D7pmkk83FT4lMAw+bvMDuRxA7f0t2s=
|
||||||
k8s.io/client-go v0.20.0 h1:Xlax8PKbZsjX4gFvNtt4F5MoJ1V5prDvCuoq9B7iax0=
|
k8s.io/client-go v0.20.0 h1:Xlax8PKbZsjX4gFvNtt4F5MoJ1V5prDvCuoq9B7iax0=
|
||||||
k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY=
|
k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY=
|
||||||
|
k8s.io/cloud-provider v0.20.0 h1:CVPQ66iyfNgeGomUq2jE/TWrfzE77bdCpemhFS8955U=
|
||||||
k8s.io/cloud-provider v0.20.0/go.mod h1:Lz/luSVD5BrHDDhtVdjFh0C2qQCRYdf0b9BHQ9L+bXc=
|
k8s.io/cloud-provider v0.20.0/go.mod h1:Lz/luSVD5BrHDDhtVdjFh0C2qQCRYdf0b9BHQ9L+bXc=
|
||||||
k8s.io/cluster-bootstrap v0.20.0/go.mod h1:6WZaNIBvcvL7MkPzSRKrZDIr4u+ePW2oIWoRsEFMjmE=
|
k8s.io/cluster-bootstrap v0.20.0/go.mod h1:6WZaNIBvcvL7MkPzSRKrZDIr4u+ePW2oIWoRsEFMjmE=
|
||||||
k8s.io/code-generator v0.20.0/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=
|
k8s.io/code-generator v0.20.0/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=
|
||||||
k8s.io/component-base v0.20.0 h1:BXGL8iitIQD+0NgW49UsM7MraNUUGDU3FBmrfUAtmVQ=
|
k8s.io/component-base v0.20.0 h1:BXGL8iitIQD+0NgW49UsM7MraNUUGDU3FBmrfUAtmVQ=
|
||||||
k8s.io/component-base v0.20.0/go.mod h1:wKPj+RHnAr8LW2EIBIK7AxOHPde4gme2lzXwVSoRXeA=
|
k8s.io/component-base v0.20.0/go.mod h1:wKPj+RHnAr8LW2EIBIK7AxOHPde4gme2lzXwVSoRXeA=
|
||||||
|
k8s.io/component-helpers v0.20.0-alpha.2.0.20201114090304-7cb42b694587 h1:XV8zcD26n/hrYX129A4vR35MvgGPKKSVAQP38dif/HI=
|
||||||
k8s.io/component-helpers v0.20.0-alpha.2.0.20201114090304-7cb42b694587/go.mod h1:Fb+7xcdRsPnQNQnXkqMwptC6LsONZr10ris8YPJw1bk=
|
k8s.io/component-helpers v0.20.0-alpha.2.0.20201114090304-7cb42b694587/go.mod h1:Fb+7xcdRsPnQNQnXkqMwptC6LsONZr10ris8YPJw1bk=
|
||||||
k8s.io/controller-manager v0.20.0-alpha.1.0.20201209052538-b2c380a1dc86 h1:22Cxrg/eu1vIlWan5HoJJzZw8/sAZpjlwMrs87uOdEQ=
|
k8s.io/controller-manager v0.20.0-alpha.1.0.20201209052538-b2c380a1dc86 h1:22Cxrg/eu1vIlWan5HoJJzZw8/sAZpjlwMrs87uOdEQ=
|
||||||
k8s.io/controller-manager v0.20.0-alpha.1.0.20201209052538-b2c380a1dc86/go.mod h1:nPjStmyrmF6IE6bhIsSN+5A4nYp1ORzLENSAPp+tSzs=
|
k8s.io/controller-manager v0.20.0-alpha.1.0.20201209052538-b2c380a1dc86/go.mod h1:nPjStmyrmF6IE6bhIsSN+5A4nYp1ORzLENSAPp+tSzs=
|
||||||
@ -1265,6 +1267,7 @@ k8s.io/kubernetes v1.21.0-alpha.0.0.20201210005053-f58c4d8cd725 h1:zDSrjDDKNWvXy
|
|||||||
k8s.io/kubernetes v1.21.0-alpha.0.0.20201210005053-f58c4d8cd725/go.mod h1:/xrHGNfoQphtkhZvyd5bA1lRmz+QkDVmBZu+O8QMoek=
|
k8s.io/kubernetes v1.21.0-alpha.0.0.20201210005053-f58c4d8cd725/go.mod h1:/xrHGNfoQphtkhZvyd5bA1lRmz+QkDVmBZu+O8QMoek=
|
||||||
k8s.io/legacy-cloud-providers v0.20.0/go.mod h1:1jEkaU7h9+b1EYdfWDBvhFAr+QpRfUjQfK+dGhxPGfA=
|
k8s.io/legacy-cloud-providers v0.20.0/go.mod h1:1jEkaU7h9+b1EYdfWDBvhFAr+QpRfUjQfK+dGhxPGfA=
|
||||||
k8s.io/metrics v0.20.0/go.mod h1:9yiRhfr8K8sjdj2EthQQE9WvpYDvsXIV3CjN4Ruq4Jw=
|
k8s.io/metrics v0.20.0/go.mod h1:9yiRhfr8K8sjdj2EthQQE9WvpYDvsXIV3CjN4Ruq4Jw=
|
||||||
|
k8s.io/mount-utils v0.21.0-alpha.0 h1:IKn8GtClgNVIb801UbMpDI7F49D6KBXzSf5yTO5VZpo=
|
||||||
k8s.io/mount-utils v0.21.0-alpha.0/go.mod h1:Jv9NRZ5L2LF87A17GaGlArD+r3JAJdZFvo4XD1cG4Kc=
|
k8s.io/mount-utils v0.21.0-alpha.0/go.mod h1:Jv9NRZ5L2LF87A17GaGlArD+r3JAJdZFvo4XD1cG4Kc=
|
||||||
k8s.io/repo-infra v0.0.1-alpha.1/go.mod h1:wO1t9WaB99V80ljbeENTnayuEEwNZt7gECYh/CEyOJ8=
|
k8s.io/repo-infra v0.0.1-alpha.1/go.mod h1:wO1t9WaB99V80ljbeENTnayuEEwNZt7gECYh/CEyOJ8=
|
||||||
k8s.io/sample-apiserver v0.20.0/go.mod h1:tScvbz/BcUG46IOsu2YLt4EjBP7XeUuMzMbQt2tQYWw=
|
k8s.io/sample-apiserver v0.20.0/go.mod h1:tScvbz/BcUG46IOsu2YLt4EjBP7XeUuMzMbQt2tQYWw=
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
"k8s.io/utils/mount"
|
"k8s.io/utils/mount"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -148,8 +149,69 @@ func (ns *NodeServer) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetC
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NodeGetVolumeStats get volume stats
|
// NodeGetVolumeStats get volume stats
|
||||||
func (ns *NodeServer) NodeGetVolumeStats(ctx context.Context, in *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) {
|
func (ns *NodeServer) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) {
|
||||||
return nil, status.Error(codes.Unimplemented, "")
|
if len(req.VolumeId) == 0 {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "NodeGetVolumeStats volume ID was empty")
|
||||||
|
}
|
||||||
|
if len(req.VolumePath) == 0 {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "NodeGetVolumeStats volume path was empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := os.Stat(req.VolumePath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, status.Errorf(codes.NotFound, "path %s does not exist", req.VolumePath)
|
||||||
|
}
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to stat file %s: %v", req.VolumePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
volumeMetrics, err := volume.NewMetricsStatFS(req.VolumePath).GetMetrics()
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to get metrics: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
available, ok := volumeMetrics.Available.AsInt64()
|
||||||
|
if !ok {
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to transform volume available size(%v)", volumeMetrics.Available)
|
||||||
|
}
|
||||||
|
capacity, ok := volumeMetrics.Capacity.AsInt64()
|
||||||
|
if !ok {
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to transform volume capacity size(%v)", volumeMetrics.Capacity)
|
||||||
|
}
|
||||||
|
used, ok := volumeMetrics.Used.AsInt64()
|
||||||
|
if !ok {
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to transform volume used size(%v)", volumeMetrics.Used)
|
||||||
|
}
|
||||||
|
|
||||||
|
inodesFree, ok := volumeMetrics.InodesFree.AsInt64()
|
||||||
|
if !ok {
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to transform disk inodes free(%v)", volumeMetrics.InodesFree)
|
||||||
|
}
|
||||||
|
inodes, ok := volumeMetrics.Inodes.AsInt64()
|
||||||
|
if !ok {
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to transform disk inodes(%v)", volumeMetrics.Inodes)
|
||||||
|
}
|
||||||
|
inodesUsed, ok := volumeMetrics.InodesUsed.AsInt64()
|
||||||
|
if !ok {
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to transform disk inodes used(%v)", volumeMetrics.InodesUsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &csi.NodeGetVolumeStatsResponse{
|
||||||
|
Usage: []*csi.VolumeUsage{
|
||||||
|
{
|
||||||
|
Unit: csi.VolumeUsage_BYTES,
|
||||||
|
Available: available,
|
||||||
|
Total: capacity,
|
||||||
|
Used: used,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Unit: csi.VolumeUsage_INODES,
|
||||||
|
Available: inodesFree,
|
||||||
|
Total: inodes,
|
||||||
|
Used: inodesUsed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeUnstageVolume unstage volume
|
// NodeUnstageVolume unstage volume
|
||||||
|
|||||||
9
vendor/k8s.io/cloud-provider/CONTRIBUTING.md
generated
vendored
Normal file
9
vendor/k8s.io/cloud-provider/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Contributing guidelines
|
||||||
|
|
||||||
|
Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md).
|
||||||
|
|
||||||
|
Do not open pull requests directly against this repository, they will be ignored. Instead, please open pull requests against [kubernetes/kubernetes](https://git.k8s.io/kubernetes/). Please follow the same [contributing guide](https://git.k8s.io/kubernetes/CONTRIBUTING.md) you would follow for any other pull request made to kubernetes/kubernetes. Changes to this repo should be discussed with [sig cloud-provider](https://github.com/kubernetes/community/tree/master/sig-cloud-provider).
|
||||||
|
|
||||||
|
This repository is published from [kubernetes/kubernetes/staging/src/k8s.io/cloud-provider](https://git.k8s.io/kubernetes/staging/src/k8s.io/cloud-provider) by the [kubernetes publishing-bot](https://git.k8s.io/publishing-bot).
|
||||||
|
|
||||||
|
Please see [Staging Directory and Publishing](https://git.k8s.io/community/contributors/devel/sig-architecture/staging.md) for more information
|
||||||
201
vendor/k8s.io/cloud-provider/LICENSE
generated
vendored
Normal file
201
vendor/k8s.io/cloud-provider/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
38
vendor/k8s.io/cloud-provider/OWNERS
generated
vendored
Normal file
38
vendor/k8s.io/cloud-provider/OWNERS
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# See the OWNERS docs at https://go.k8s.io/owners
|
||||||
|
|
||||||
|
approvers:
|
||||||
|
- mikedanese
|
||||||
|
- dims
|
||||||
|
- wlan0
|
||||||
|
- andrewsykim
|
||||||
|
- cheftako
|
||||||
|
reviewers:
|
||||||
|
- wojtek-t
|
||||||
|
- deads2k
|
||||||
|
- derekwaynecarr
|
||||||
|
- vishh
|
||||||
|
- mikedanese
|
||||||
|
- liggitt
|
||||||
|
- gmarek
|
||||||
|
- davidopp
|
||||||
|
- pmorie
|
||||||
|
- sttts
|
||||||
|
- quinton-hoole
|
||||||
|
- dchen1107
|
||||||
|
- saad-ali
|
||||||
|
- luxas
|
||||||
|
- justinsb
|
||||||
|
- piosz
|
||||||
|
- jsafrane
|
||||||
|
- dims
|
||||||
|
- krousey
|
||||||
|
- rootfs
|
||||||
|
- freehan
|
||||||
|
- jingxu97
|
||||||
|
- wlan0
|
||||||
|
- cheftako
|
||||||
|
- andrewsykim
|
||||||
|
- mcrute
|
||||||
|
labels:
|
||||||
|
- sig/cloud-provider
|
||||||
|
- area/cloudprovider
|
||||||
32
vendor/k8s.io/cloud-provider/README.md
generated
vendored
Normal file
32
vendor/k8s.io/cloud-provider/README.md
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# cloud-provider
|
||||||
|
|
||||||
|
This repository defines the cloud-provider interface and mechanism to initialize
|
||||||
|
a cloud-provider implementation into Kubernetes. Currently multiple processes
|
||||||
|
use this code although the intent is that it will eventually only be cloud
|
||||||
|
controller manager.
|
||||||
|
|
||||||
|
**Note:** go-get or vendor this package as `k8s.io/cloud-provider`.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This library is a shared dependency for processes which need to be able to
|
||||||
|
integrate with cloud-provider specific functionality.
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
Cloud Providers are expected to keep the HEAD of their implementations in sync
|
||||||
|
with the HEAD of this repository.
|
||||||
|
|
||||||
|
## Where does it come from?
|
||||||
|
|
||||||
|
`cloud-provider` is synced from
|
||||||
|
https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/cloud-provider.
|
||||||
|
Code changes are made in that location, merged into k8s.io/kubernetes and
|
||||||
|
later synced here.
|
||||||
|
|
||||||
|
## Things you should NOT do
|
||||||
|
|
||||||
|
1. Add an cloud provider specific code to this repo.
|
||||||
|
2. Directly modify anything under vendor/k8s.io/cloud-provider in this repo. Those are driven from `k8s.io/kubernetes/staging/src/k8s.io/cloud-provider`.
|
||||||
|
3. Make interface changes without first discussing them with
|
||||||
|
sig-cloudprovider.
|
||||||
20
vendor/k8s.io/cloud-provider/SECURITY_CONTACTS
generated
vendored
Normal file
20
vendor/k8s.io/cloud-provider/SECURITY_CONTACTS
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Defined below are the security contacts for this repo.
|
||||||
|
#
|
||||||
|
# They are the contact point for the Product Security Committee to reach out
|
||||||
|
# to for triaging and handling of incoming issues.
|
||||||
|
#
|
||||||
|
# The below names agree to abide by the
|
||||||
|
# [Embargo Policy](https://git.k8s.io/security/private-distributors-list.md#embargo-policy)
|
||||||
|
# and will be removed and replaced if they violate that agreement.
|
||||||
|
#
|
||||||
|
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
|
||||||
|
# INSTRUCTIONS AT https://kubernetes.io/security/
|
||||||
|
|
||||||
|
cheftako
|
||||||
|
andrewsykim
|
||||||
|
dims
|
||||||
|
cjcullen
|
||||||
|
joelsmith
|
||||||
|
liggitt
|
||||||
|
philips
|
||||||
|
tallclair
|
||||||
313
vendor/k8s.io/cloud-provider/cloud.go
generated
vendored
Normal file
313
vendor/k8s.io/cloud-provider/cloud.go
generated
vendored
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cloudprovider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/client-go/informers"
|
||||||
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
|
restclient "k8s.io/client-go/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ControllerClientBuilder allows you to get clients and configs for controllers
|
||||||
|
// Please note a copy also exists in pkg/controller/client_builder.go
|
||||||
|
// TODO: Make this depend on the separate controller utilities repo (issues/68947)
|
||||||
|
type ControllerClientBuilder interface {
|
||||||
|
Config(name string) (*restclient.Config, error)
|
||||||
|
ConfigOrDie(name string) *restclient.Config
|
||||||
|
Client(name string) (clientset.Interface, error)
|
||||||
|
ClientOrDie(name string) clientset.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface is an abstract, pluggable interface for cloud providers.
|
||||||
|
type Interface interface {
|
||||||
|
// Initialize provides the cloud with a kubernetes client builder and may spawn goroutines
|
||||||
|
// to perform housekeeping or run custom controllers specific to the cloud provider.
|
||||||
|
// Any tasks started here should be cleaned up when the stop channel closes.
|
||||||
|
Initialize(clientBuilder ControllerClientBuilder, stop <-chan struct{})
|
||||||
|
// LoadBalancer returns a balancer interface. Also returns true if the interface is supported, false otherwise.
|
||||||
|
LoadBalancer() (LoadBalancer, bool)
|
||||||
|
// Instances returns an instances interface. Also returns true if the interface is supported, false otherwise.
|
||||||
|
Instances() (Instances, bool)
|
||||||
|
// InstancesV2 is an implementation for instances and should only be implemented by external cloud providers.
|
||||||
|
// Implementing InstancesV2 is behaviorally identical to Instances but is optimized to significantly reduce
|
||||||
|
// API calls to the cloud provider when registering and syncing nodes. Implementation of this interface will
|
||||||
|
// disable calls to the Zones interface. Also returns true if the interface is supported, false otherwise.
|
||||||
|
InstancesV2() (InstancesV2, bool)
|
||||||
|
// Zones returns a zones interface. Also returns true if the interface is supported, false otherwise.
|
||||||
|
// DEPRECATED: Zones is deprecated in favor of retrieving zone/region information from InstancesV2.
|
||||||
|
// This interface will not be called if InstancesV2 is enabled.
|
||||||
|
Zones() (Zones, bool)
|
||||||
|
// Clusters returns a clusters interface. Also returns true if the interface is supported, false otherwise.
|
||||||
|
Clusters() (Clusters, bool)
|
||||||
|
// Routes returns a routes interface along with whether the interface is supported.
|
||||||
|
Routes() (Routes, bool)
|
||||||
|
// ProviderName returns the cloud provider ID.
|
||||||
|
ProviderName() string
|
||||||
|
// HasClusterID returns true if a ClusterID is required and set
|
||||||
|
HasClusterID() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type InformerUser interface {
|
||||||
|
// SetInformers sets the informer on the cloud object.
|
||||||
|
SetInformers(informerFactory informers.SharedInformerFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clusters is an abstract, pluggable interface for clusters of containers.
|
||||||
|
type Clusters interface {
|
||||||
|
// ListClusters lists the names of the available clusters.
|
||||||
|
ListClusters(ctx context.Context) ([]string, error)
|
||||||
|
// Master gets back the address (either DNS name or IP address) of the master node for the cluster.
|
||||||
|
Master(ctx context.Context, clusterName string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// (DEPRECATED) DefaultLoadBalancerName is the default load balancer name that is called from
|
||||||
|
// LoadBalancer.GetLoadBalancerName. Use this method to maintain backward compatible names for
|
||||||
|
// LoadBalancers that were created prior to Kubernetes v1.12. In the future, each provider should
|
||||||
|
// replace this method call in GetLoadBalancerName with a provider-specific implementation that
|
||||||
|
// is less cryptic than the Service's UUID.
|
||||||
|
func DefaultLoadBalancerName(service *v1.Service) string {
|
||||||
|
//GCE requires that the name of a load balancer starts with a lower case letter.
|
||||||
|
ret := "a" + string(service.UID)
|
||||||
|
ret = strings.Replace(ret, "-", "", -1)
|
||||||
|
//AWS requires that the name of a load balancer is shorter than 32 bytes.
|
||||||
|
if len(ret) > 32 {
|
||||||
|
ret = ret[:32]
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInstanceProviderID builds a ProviderID for a node in a cloud.
|
||||||
|
func GetInstanceProviderID(ctx context.Context, cloud Interface, nodeName types.NodeName) (string, error) {
|
||||||
|
instances, ok := cloud.Instances()
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("failed to get instances from cloud provider")
|
||||||
|
}
|
||||||
|
instanceID, err := instances.InstanceID(ctx, nodeName)
|
||||||
|
if err != nil {
|
||||||
|
if err == NotImplemented {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("failed to get instance ID from cloud provider: %v", err)
|
||||||
|
}
|
||||||
|
return cloud.ProviderName() + "://" + instanceID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadBalancer is an abstract, pluggable interface for load balancers.
|
||||||
|
//
|
||||||
|
// Cloud provider may chose to implement the logic for
|
||||||
|
// constructing/destroying specific kinds of load balancers in a
|
||||||
|
// controller separate from the ServiceController. If this is the case,
|
||||||
|
// then {Ensure,Update}LoadBalancer must return the ImplementedElsewhere error.
|
||||||
|
// For the given LB service, the GetLoadBalancer must return "exists=True" if
|
||||||
|
// there exists a LoadBalancer instance created by ServiceController.
|
||||||
|
// In all other cases, GetLoadBalancer must return a NotFound error.
|
||||||
|
// EnsureLoadBalancerDeleted must not return ImplementedElsewhere to ensure
|
||||||
|
// proper teardown of resources that were allocated by the ServiceController.
|
||||||
|
// This can happen if a user changes the type of LB via an update to the resource
|
||||||
|
// or when migrating from ServiceController to alternate implementation.
|
||||||
|
// The finalizer on the service will be added and removed by ServiceController
|
||||||
|
// irrespective of the ImplementedElsewhere error. Additional finalizers for
|
||||||
|
// LB services must be managed in the alternate implementation.
|
||||||
|
type LoadBalancer interface {
|
||||||
|
// TODO: Break this up into different interfaces (LB, etc) when we have more than one type of service
|
||||||
|
// GetLoadBalancer returns whether the specified load balancer exists, and
|
||||||
|
// if so, what its status is.
|
||||||
|
// Implementations must treat the *v1.Service parameter as read-only and not modify it.
|
||||||
|
// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
|
||||||
|
GetLoadBalancer(ctx context.Context, clusterName string, service *v1.Service) (status *v1.LoadBalancerStatus, exists bool, err error)
|
||||||
|
// GetLoadBalancerName returns the name of the load balancer. Implementations must treat the
|
||||||
|
// *v1.Service parameter as read-only and not modify it.
|
||||||
|
GetLoadBalancerName(ctx context.Context, clusterName string, service *v1.Service) string
|
||||||
|
// EnsureLoadBalancer creates a new load balancer 'name', or updates the existing one. Returns the status of the balancer
|
||||||
|
// Implementations must treat the *v1.Service and *v1.Node
|
||||||
|
// parameters as read-only and not modify them.
|
||||||
|
// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
|
||||||
|
EnsureLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) (*v1.LoadBalancerStatus, error)
|
||||||
|
// UpdateLoadBalancer updates hosts under the specified load balancer.
|
||||||
|
// Implementations must treat the *v1.Service and *v1.Node
|
||||||
|
// parameters as read-only and not modify them.
|
||||||
|
// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
|
||||||
|
UpdateLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) error
|
||||||
|
// EnsureLoadBalancerDeleted deletes the specified load balancer if it
|
||||||
|
// exists, returning nil if the load balancer specified either didn't exist or
|
||||||
|
// was successfully deleted.
|
||||||
|
// This construction is useful because many cloud providers' load balancers
|
||||||
|
// have multiple underlying components, meaning a Get could say that the LB
|
||||||
|
// doesn't exist even if some part of it is still laying around.
|
||||||
|
// Implementations must treat the *v1.Service parameter as read-only and not modify it.
|
||||||
|
// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
|
||||||
|
EnsureLoadBalancerDeleted(ctx context.Context, clusterName string, service *v1.Service) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instances is an abstract, pluggable interface for sets of instances.
|
||||||
|
type Instances interface {
|
||||||
|
// NodeAddresses returns the addresses of the specified instance.
|
||||||
|
NodeAddresses(ctx context.Context, name types.NodeName) ([]v1.NodeAddress, error)
|
||||||
|
// NodeAddressesByProviderID returns the addresses of the specified instance.
|
||||||
|
// The instance is specified using the providerID of the node. The
|
||||||
|
// ProviderID is a unique identifier of the node. This will not be called
|
||||||
|
// from the node whose nodeaddresses are being queried. i.e. local metadata
|
||||||
|
// services cannot be used in this method to obtain nodeaddresses
|
||||||
|
NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error)
|
||||||
|
// InstanceID returns the cloud provider ID of the node with the specified NodeName.
|
||||||
|
// Note that if the instance does not exist, we must return ("", cloudprovider.InstanceNotFound)
|
||||||
|
// cloudprovider.InstanceNotFound should NOT be returned for instances that exist but are stopped/sleeping
|
||||||
|
InstanceID(ctx context.Context, nodeName types.NodeName) (string, error)
|
||||||
|
// InstanceType returns the type of the specified instance.
|
||||||
|
InstanceType(ctx context.Context, name types.NodeName) (string, error)
|
||||||
|
// InstanceTypeByProviderID returns the type of the specified instance.
|
||||||
|
InstanceTypeByProviderID(ctx context.Context, providerID string) (string, error)
|
||||||
|
// AddSSHKeyToAllInstances adds an SSH public key as a legal identity for all instances
|
||||||
|
// expected format for the key is standard ssh-keygen format: <protocol> <blob>
|
||||||
|
AddSSHKeyToAllInstances(ctx context.Context, user string, keyData []byte) error
|
||||||
|
// CurrentNodeName returns the name of the node we are currently running on
|
||||||
|
// On most clouds (e.g. GCE) this is the hostname, so we provide the hostname
|
||||||
|
CurrentNodeName(ctx context.Context, hostname string) (types.NodeName, error)
|
||||||
|
// InstanceExistsByProviderID returns true if the instance for the given provider exists.
|
||||||
|
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
|
||||||
|
// This method should still return true for instances that exist but are stopped/sleeping.
|
||||||
|
InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error)
|
||||||
|
// InstanceShutdownByProviderID returns true if the instance is shutdown in cloudprovider
|
||||||
|
InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstancesV2 is an abstract, pluggable interface for cloud provider instances.
|
||||||
|
// Unlike the Instances interface, it is designed for external cloud providers and should only be used by them.
|
||||||
|
// Implementation of this interface will disable calls to the Zones interface.
|
||||||
|
type InstancesV2 interface {
|
||||||
|
// InstanceExists returns true if the instance for the given node exists according to the cloud provider.
|
||||||
|
// Use the node.name or node.spec.providerID field to find the node in the cloud provider.
|
||||||
|
InstanceExists(ctx context.Context, node *v1.Node) (bool, error)
|
||||||
|
// InstanceShutdown returns true if the instance is shutdown according to the cloud provider.
|
||||||
|
// Use the node.name or node.spec.providerID field to find the node in the cloud provider.
|
||||||
|
InstanceShutdown(ctx context.Context, node *v1.Node) (bool, error)
|
||||||
|
// InstanceMetadata returns the instance's metadata. The values returned in InstanceMetadata are
|
||||||
|
// translated into specific fields and labels in the Node object on registration.
|
||||||
|
// Implementations should always check node.spec.providerID first when trying to discover the instance
|
||||||
|
// for a given node. In cases where node.spec.providerID is empty, implementations can use other
|
||||||
|
// properties of the node like its name, labels and annotations.
|
||||||
|
InstanceMetadata(ctx context.Context, node *v1.Node) (*InstanceMetadata, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route is a representation of an advanced routing rule.
|
||||||
|
type Route struct {
|
||||||
|
// Name is the name of the routing rule in the cloud-provider.
|
||||||
|
// It will be ignored in a Create (although nameHint may influence it)
|
||||||
|
Name string
|
||||||
|
// TargetNode is the NodeName of the target instance.
|
||||||
|
TargetNode types.NodeName
|
||||||
|
// DestinationCIDR is the CIDR format IP range that this routing rule
|
||||||
|
// applies to.
|
||||||
|
DestinationCIDR string
|
||||||
|
// Blackhole is set to true if this is a blackhole route
|
||||||
|
// The node controller will delete the route if it is in the managed range.
|
||||||
|
Blackhole bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Routes is an abstract, pluggable interface for advanced routing rules.
|
||||||
|
type Routes interface {
|
||||||
|
// ListRoutes lists all managed routes that belong to the specified clusterName
|
||||||
|
ListRoutes(ctx context.Context, clusterName string) ([]*Route, error)
|
||||||
|
// CreateRoute creates the described managed route
|
||||||
|
// route.Name will be ignored, although the cloud-provider may use nameHint
|
||||||
|
// to create a more user-meaningful name.
|
||||||
|
CreateRoute(ctx context.Context, clusterName string, nameHint string, route *Route) error
|
||||||
|
// DeleteRoute deletes the specified managed route
|
||||||
|
// Route should be as returned by ListRoutes
|
||||||
|
DeleteRoute(ctx context.Context, clusterName string, route *Route) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
DiskNotFound = errors.New("disk is not found")
|
||||||
|
ImplementedElsewhere = errors.New("implemented by alternate to cloud provider")
|
||||||
|
InstanceNotFound = errors.New("instance not found")
|
||||||
|
NotImplemented = errors.New("unimplemented")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Zone represents the location of a particular machine.
|
||||||
|
type Zone struct {
|
||||||
|
FailureDomain string
|
||||||
|
Region string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zones is an abstract, pluggable interface for zone enumeration.
|
||||||
|
// DEPRECATED: Zones is deprecated in favor of retrieving zone/region information from InstancesV2.
|
||||||
|
// This interface will not be called if InstancesV2 is enabled.
|
||||||
|
type Zones interface {
|
||||||
|
// GetZone returns the Zone containing the current failure zone and locality region that the program is running in
|
||||||
|
// In most cases, this method is called from the kubelet querying a local metadata service to acquire its zone.
|
||||||
|
// For the case of external cloud providers, use GetZoneByProviderID or GetZoneByNodeName since GetZone
|
||||||
|
// can no longer be called from the kubelets.
|
||||||
|
GetZone(ctx context.Context) (Zone, error)
|
||||||
|
|
||||||
|
// GetZoneByProviderID returns the Zone containing the current zone and locality region of the node specified by providerID
|
||||||
|
// This method is particularly used in the context of external cloud providers where node initialization must be done
|
||||||
|
// outside the kubelets.
|
||||||
|
GetZoneByProviderID(ctx context.Context, providerID string) (Zone, error)
|
||||||
|
|
||||||
|
// GetZoneByNodeName returns the Zone containing the current zone and locality region of the node specified by node name
|
||||||
|
// This method is particularly used in the context of external cloud providers where node initialization must be done
|
||||||
|
// outside the kubelets.
|
||||||
|
GetZoneByNodeName(ctx context.Context, nodeName types.NodeName) (Zone, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PVLabeler is an abstract, pluggable interface for fetching labels for volumes
|
||||||
|
type PVLabeler interface {
|
||||||
|
GetLabelsForVolume(ctx context.Context, pv *v1.PersistentVolume) (map[string]string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceMetadata contains metadata about a specific instance.
|
||||||
|
// Values returned in InstanceMetadata are translated into specific fields and labels for Node.
|
||||||
|
type InstanceMetadata struct {
|
||||||
|
// ProviderID is a unique ID used to idenfitify an instance on the cloud provider.
|
||||||
|
// The ProviderID set here will be set on the node's spec.providerID field.
|
||||||
|
// The provider ID format can be set by the cloud provider but providers should
|
||||||
|
// ensure the format does not change in any incompatible way.
|
||||||
|
//
|
||||||
|
// The provider ID format used by existing cloud provider has been:
|
||||||
|
// <provider-name>://<instance-id>
|
||||||
|
// Existing providers setting this field should preserve the existing format
|
||||||
|
// currently being set in node.spec.providerID.
|
||||||
|
ProviderID string
|
||||||
|
// InstanceType is the instance's type.
|
||||||
|
// The InstanceType set here will be set using the following labels on the node object:
|
||||||
|
// * node.kubernetes.io/instance-type=<instance-type>
|
||||||
|
// * beta.kubernetes.io/instance-type=<instance-type> (DEPRECATED)
|
||||||
|
InstanceType string
|
||||||
|
// NodeAddress contains information for the instance's address.
|
||||||
|
// The node addresses returned here will be set on the node's status.addresses field.
|
||||||
|
NodeAddresses []v1.NodeAddress
|
||||||
|
|
||||||
|
// Zone is the zone that the instance is in.
|
||||||
|
// The value set here is applied as the following labels on the node:
|
||||||
|
// * topology.kubernetes.io/zone=<zone>
|
||||||
|
// * failure-domain.beta.kubernetes.io/zone=<zone> (DEPRECATED)
|
||||||
|
Zone string
|
||||||
|
// Region is the region that the instance is in.
|
||||||
|
// The value set here is applied as the following labels on the node:
|
||||||
|
// * topology.kubernetes.io/region=<region>
|
||||||
|
// * failure-domain.beta.kubernetes.io/region=<region> (DEPRECATED)
|
||||||
|
Region string
|
||||||
|
}
|
||||||
3
vendor/k8s.io/cloud-provider/code-of-conduct.md
generated
vendored
Normal file
3
vendor/k8s.io/cloud-provider/code-of-conduct.md
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Kubernetes Community Code of Conduct
|
||||||
|
|
||||||
|
Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)
|
||||||
18
vendor/k8s.io/cloud-provider/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/cloud-provider/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package cloudprovider supplies interfaces and implementations for cloud service providers.
|
||||||
|
package cloudprovider // import "k8s.io/cloud-provider"
|
||||||
29
vendor/k8s.io/cloud-provider/go.mod
generated
vendored
Normal file
29
vendor/k8s.io/cloud-provider/go.mod
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// This is a generated file. Do not edit directly.
|
||||||
|
|
||||||
|
module k8s.io/cloud-provider
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/google/go-cmp v0.5.2
|
||||||
|
github.com/spf13/cobra v1.1.1
|
||||||
|
github.com/spf13/pflag v1.0.5
|
||||||
|
github.com/stretchr/testify v1.6.1
|
||||||
|
k8s.io/api v0.20.0
|
||||||
|
k8s.io/apimachinery v0.20.0
|
||||||
|
k8s.io/apiserver v0.20.0
|
||||||
|
k8s.io/client-go v0.20.0
|
||||||
|
k8s.io/component-base v0.20.0
|
||||||
|
k8s.io/controller-manager v0.20.0
|
||||||
|
k8s.io/klog/v2 v2.4.0
|
||||||
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920
|
||||||
|
)
|
||||||
|
|
||||||
|
replace (
|
||||||
|
k8s.io/api => k8s.io/api v0.20.0
|
||||||
|
k8s.io/apimachinery => k8s.io/apimachinery v0.20.0
|
||||||
|
k8s.io/apiserver => k8s.io/apiserver v0.20.0
|
||||||
|
k8s.io/client-go => k8s.io/client-go v0.20.0
|
||||||
|
k8s.io/component-base => k8s.io/component-base v0.20.0
|
||||||
|
k8s.io/controller-manager => k8s.io/controller-manager v0.20.0
|
||||||
|
)
|
||||||
694
vendor/k8s.io/cloud-provider/go.sum
generated
vendored
Normal file
694
vendor/k8s.io/cloud-provider/go.sum
generated
vendored
Normal file
@ -0,0 +1,694 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
|
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||||
|
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||||
|
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||||
|
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
|
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
|
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||||
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||||
|
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
|
github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
|
||||||
|
github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
|
||||||
|
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
|
||||||
|
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||||
|
github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||||
|
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||||
|
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||||
|
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||||
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0=
|
||||||
|
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||||
|
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
|
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||||
|
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/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/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
|
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
|
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||||
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||||
|
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||||
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
|
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||||
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||||
|
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||||
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||||
|
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/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
|
||||||
|
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
|
||||||
|
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||||
|
github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY=
|
||||||
|
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
|
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||||
|
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
|
||||||
|
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||||
|
github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc=
|
||||||
|
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||||
|
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
|
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||||
|
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
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.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=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA=
|
||||||
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
|
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||||
|
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I=
|
||||||
|
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||||
|
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
|
||||||
|
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||||
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||||
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
|
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||||
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
|
||||||
|
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI=
|
||||||
|
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||||
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
|
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
|
||||||
|
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
|
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||||
|
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
|
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||||
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
|
github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
|
||||||
|
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||||
|
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
|
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
|
||||||
|
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
|
||||||
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
|
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
|
||||||
|
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||||
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
|
||||||
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||||
|
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
|
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
|
||||||
|
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
||||||
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||||
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
|
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
|
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
||||||
|
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||||
|
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo=
|
||||||
|
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
|
||||||
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
|
||||||
|
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
|
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||||
|
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
|
||||||
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
|
||||||
|
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
|
||||||
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
|
||||||
|
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8=
|
||||||
|
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.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.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
|
||||||
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
|
||||||
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
|
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||||
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
|
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
|
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||||
|
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||||
|
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
k8s.io/api v0.20.0/go.mod h1:HyLC5l5eoS/ygQYl1BXBgFzWNlkHiAuyNAbevIn+FKg=
|
||||||
|
k8s.io/apimachinery v0.20.0/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
|
||||||
|
k8s.io/apiserver v0.20.0/go.mod h1:6gRIWiOkvGvQt12WTYmsiYoUyYW0FXSiMdNl4m+sxY8=
|
||||||
|
k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY=
|
||||||
|
k8s.io/component-base v0.20.0/go.mod h1:wKPj+RHnAr8LW2EIBIK7AxOHPde4gme2lzXwVSoRXeA=
|
||||||
|
k8s.io/controller-manager v0.20.0/go.mod h1:nD4qym/pmCz2v1tpqvlEBVlHW9CAZwedloM8GrJTLpg=
|
||||||
|
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||||
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
|
k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
|
||||||
|
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||||
|
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c=
|
||||||
|
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
|
||||||
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
|
||||||
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||||
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14 h1:TihvEz9MPj2u0KWds6E2OBUXfwaL4qRJ33c7HGiJpqk=
|
||||||
|
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8=
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
|
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||||
|
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||||
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
148
vendor/k8s.io/cloud-provider/plugins.go
generated
vendored
Normal file
148
vendor/k8s.io/cloud-provider/plugins.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cloudprovider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Factory is a function that returns a cloudprovider.Interface.
|
||||||
|
// The config parameter provides an io.Reader handler to the factory in
|
||||||
|
// order to load specific configurations. If no configuration is provided
|
||||||
|
// the parameter is nil.
|
||||||
|
type Factory func(config io.Reader) (Interface, error)
|
||||||
|
|
||||||
|
// All registered cloud providers.
|
||||||
|
var (
|
||||||
|
providersMutex sync.Mutex
|
||||||
|
providers = make(map[string]Factory)
|
||||||
|
deprecatedCloudProviders = []struct {
|
||||||
|
name string
|
||||||
|
external bool
|
||||||
|
detail string
|
||||||
|
}{
|
||||||
|
{"aws", false, "The AWS provider is deprecated and will be removed in a future release"},
|
||||||
|
{"azure", false, "The Azure provider is deprecated and will be removed in a future release"},
|
||||||
|
{"gce", false, "The GCE provider is deprecated and will be removed in a future release"},
|
||||||
|
{"openstack", true, "https://github.com/kubernetes/cloud-provider-openstack"},
|
||||||
|
{"vsphere", false, "The vSphere provider is deprecated and will be removed in a future release"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const externalCloudProvider = "external"
|
||||||
|
|
||||||
|
// RegisterCloudProvider registers a cloudprovider.Factory by name. This
|
||||||
|
// is expected to happen during app startup.
|
||||||
|
func RegisterCloudProvider(name string, cloud Factory) {
|
||||||
|
providersMutex.Lock()
|
||||||
|
defer providersMutex.Unlock()
|
||||||
|
if _, found := providers[name]; found {
|
||||||
|
klog.Fatalf("Cloud provider %q was registered twice", name)
|
||||||
|
}
|
||||||
|
klog.V(1).Infof("Registered cloud provider %q", name)
|
||||||
|
providers[name] = cloud
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCloudProvider returns true if name corresponds to an already registered
|
||||||
|
// cloud provider.
|
||||||
|
func IsCloudProvider(name string) bool {
|
||||||
|
providersMutex.Lock()
|
||||||
|
defer providersMutex.Unlock()
|
||||||
|
_, found := providers[name]
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCloudProvider creates an instance of the named cloud provider, or nil if
|
||||||
|
// the name is unknown. The error return is only used if the named provider
|
||||||
|
// was known but failed to initialize. The config parameter specifies the
|
||||||
|
// io.Reader handler of the configuration file for the cloud provider, or nil
|
||||||
|
// for no configuration.
|
||||||
|
func GetCloudProvider(name string, config io.Reader) (Interface, error) {
|
||||||
|
providersMutex.Lock()
|
||||||
|
defer providersMutex.Unlock()
|
||||||
|
f, found := providers[name]
|
||||||
|
if !found {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return f(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detects if the string is an external cloud provider
|
||||||
|
func IsExternal(name string) bool {
|
||||||
|
return name == externalCloudProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeprecationWarningForProvider(providerName string) {
|
||||||
|
for _, provider := range deprecatedCloudProviders {
|
||||||
|
if provider.name != providerName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
detail := provider.detail
|
||||||
|
if provider.external {
|
||||||
|
detail = fmt.Sprintf("Please use 'external' cloud provider for %s: %s", providerName, provider.detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.Warningf("WARNING: %s built-in cloud provider is now deprecated. %s", providerName, detail)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCloudProvider creates an instance of the named cloud provider.
|
||||||
|
func InitCloudProvider(name string, configFilePath string) (Interface, error) {
|
||||||
|
var cloud Interface
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsExternal(name) {
|
||||||
|
klog.Info("External cloud provider specified")
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if configFilePath != "" {
|
||||||
|
var config *os.File
|
||||||
|
config, err = os.Open(configFilePath)
|
||||||
|
if err != nil {
|
||||||
|
klog.Fatalf("Couldn't open cloud provider configuration %s: %#v",
|
||||||
|
configFilePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer config.Close()
|
||||||
|
cloud, err = GetCloudProvider(name, config)
|
||||||
|
} else {
|
||||||
|
// Pass explicit nil so plugins can actually check for nil. See
|
||||||
|
// "Why is my nil error value not equal to nil?" in golang.org/doc/faq.
|
||||||
|
cloud, err = GetCloudProvider(name, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not init cloud provider %q: %v", name, err)
|
||||||
|
}
|
||||||
|
if cloud == nil {
|
||||||
|
return nil, fmt.Errorf("unknown cloud provider %q", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cloud, nil
|
||||||
|
}
|
||||||
23
vendor/k8s.io/cloud-provider/ports.go
generated
vendored
Normal file
23
vendor/k8s.io/cloud-provider/ports.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cloudprovider
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CloudControllerManagerPort is the default port for the cloud controller manager server.
|
||||||
|
// This value may be overridden by a flag at startup.
|
||||||
|
CloudControllerManagerPort = 10258
|
||||||
|
)
|
||||||
58
vendor/k8s.io/kubernetes/pkg/proxy/util/BUILD
generated
vendored
Normal file
58
vendor/k8s.io/kubernetes/pkg/proxy/util/BUILD
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"endpoints.go",
|
||||||
|
"network.go",
|
||||||
|
"port.go",
|
||||||
|
"utils.go",
|
||||||
|
],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/proxy/util",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/apis/core/v1/helper:go_default_library",
|
||||||
|
"//pkg/util/sysctl:go_default_library",
|
||||||
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/rand:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/net:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = [
|
||||||
|
"endpoints_test.go",
|
||||||
|
"port_test.go",
|
||||||
|
"utils_test.go",
|
||||||
|
],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/proxy/util/testing:go_default_library",
|
||||||
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [
|
||||||
|
":package-srcs",
|
||||||
|
"//pkg/proxy/util/iptables:all-srcs",
|
||||||
|
"//pkg/proxy/util/testing:all-srcs",
|
||||||
|
],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
74
vendor/k8s.io/kubernetes/pkg/proxy/util/endpoints.go
generated
vendored
Normal file
74
vendor/k8s.io/kubernetes/pkg/proxy/util/endpoints.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IPPart returns just the IP part of an IP or IP:port or endpoint string. If the IP
|
||||||
|
// part is an IPv6 address enclosed in brackets (e.g. "[fd00:1::5]:9999"),
|
||||||
|
// then the brackets are stripped as well.
|
||||||
|
func IPPart(s string) string {
|
||||||
|
if ip := net.ParseIP(s); ip != nil {
|
||||||
|
// IP address without port
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
// Must be IP:port
|
||||||
|
host, _, err := net.SplitHostPort(s)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Error parsing '%s': %v", s, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
// Check if host string is a valid IP address
|
||||||
|
ip := net.ParseIP(host)
|
||||||
|
if ip == nil {
|
||||||
|
klog.Errorf("invalid IP part '%s'", host)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return ip.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortPart returns just the port part of an endpoint string.
|
||||||
|
func PortPart(s string) (int, error) {
|
||||||
|
// Must be IP:port
|
||||||
|
_, port, err := net.SplitHostPort(s)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Error parsing '%s': %v", s, err)
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
portNumber, err := strconv.Atoi(port)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Error parsing '%s': %v", port, err)
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return portNumber, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToCIDR returns a host address of the form <ip-address>/32 for
|
||||||
|
// IPv4 and <ip-address>/128 for IPv6
|
||||||
|
func ToCIDR(ip net.IP) string {
|
||||||
|
len := 32
|
||||||
|
if ip.To4() == nil {
|
||||||
|
len = 128
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s/%d", ip.String(), len)
|
||||||
|
}
|
||||||
45
vendor/k8s.io/kubernetes/pkg/proxy/util/network.go
generated
vendored
Normal file
45
vendor/k8s.io/kubernetes/pkg/proxy/util/network.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetworkInterfacer defines an interface for several net library functions. Production
|
||||||
|
// code will forward to net library functions, and unit tests will override the methods
|
||||||
|
// for testing purposes.
|
||||||
|
type NetworkInterfacer interface {
|
||||||
|
Addrs(intf *net.Interface) ([]net.Addr, error)
|
||||||
|
Interfaces() ([]net.Interface, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RealNetwork implements the NetworkInterfacer interface for production code, just
|
||||||
|
// wrapping the underlying net library function calls.
|
||||||
|
type RealNetwork struct{}
|
||||||
|
|
||||||
|
// Addrs wraps net.Interface.Addrs(), it's a part of NetworkInterfacer interface.
|
||||||
|
func (RealNetwork) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||||||
|
return intf.Addrs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interfaces wraps net.Interfaces(), it's a part of NetworkInterfacer interface.
|
||||||
|
func (RealNetwork) Interfaces() ([]net.Interface, error) {
|
||||||
|
return net.Interfaces()
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ NetworkInterfacer = &RealNetwork{}
|
||||||
67
vendor/k8s.io/kubernetes/pkg/proxy/util/port.go
generated
vendored
Normal file
67
vendor/k8s.io/kubernetes/pkg/proxy/util/port.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LocalPort describes a port on specific IP address and protocol
|
||||||
|
type LocalPort struct {
|
||||||
|
// Description is the identity message of a given local port.
|
||||||
|
Description string
|
||||||
|
// IP is the IP address part of a given local port.
|
||||||
|
// If this string is empty, the port binds to all local IP addresses.
|
||||||
|
IP string
|
||||||
|
// Port is the port part of a given local port.
|
||||||
|
Port int
|
||||||
|
// Protocol is the protocol part of a given local port.
|
||||||
|
// The value is assumed to be lower-case. For example, "udp" not "UDP", "tcp" not "TCP".
|
||||||
|
Protocol string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lp *LocalPort) String() string {
|
||||||
|
ipPort := net.JoinHostPort(lp.IP, strconv.Itoa(lp.Port))
|
||||||
|
return fmt.Sprintf("%q (%s/%s)", lp.Description, ipPort, lp.Protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closeable is an interface around closing a port.
|
||||||
|
type Closeable interface {
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortOpener is an interface around port opening/closing.
|
||||||
|
// Abstracted out for testing.
|
||||||
|
type PortOpener interface {
|
||||||
|
OpenLocalPort(lp *LocalPort, isIPv6 bool) (Closeable, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevertPorts is closing ports in replacementPortsMap but not in originalPortsMap. In other words, it only
|
||||||
|
// closes the ports opened in this sync.
|
||||||
|
func RevertPorts(replacementPortsMap, originalPortsMap map[LocalPort]Closeable) {
|
||||||
|
for k, v := range replacementPortsMap {
|
||||||
|
// Only close newly opened local ports - leave ones that were open before this update
|
||||||
|
if originalPortsMap[k] == nil {
|
||||||
|
klog.V(2).Infof("Closing local port %s", k.String())
|
||||||
|
v.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
443
vendor/k8s.io/kubernetes/pkg/proxy/util/utils.go
generated
vendored
Normal file
443
vendor/k8s.io/kubernetes/pkg/proxy/util/utils.go
generated
vendored
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
utilrand "k8s.io/apimachinery/pkg/util/rand"
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
"k8s.io/client-go/tools/record"
|
||||||
|
helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||||
|
utilsysctl "k8s.io/kubernetes/pkg/util/sysctl"
|
||||||
|
utilnet "k8s.io/utils/net"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// IPv4ZeroCIDR is the CIDR block for the whole IPv4 address space
|
||||||
|
IPv4ZeroCIDR = "0.0.0.0/0"
|
||||||
|
|
||||||
|
// IPv6ZeroCIDR is the CIDR block for the whole IPv6 address space
|
||||||
|
IPv6ZeroCIDR = "::/0"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrAddressNotAllowed indicates the address is not allowed
|
||||||
|
ErrAddressNotAllowed = errors.New("address not allowed")
|
||||||
|
|
||||||
|
// ErrNoAddresses indicates there are no addresses for the hostname
|
||||||
|
ErrNoAddresses = errors.New("No addresses for hostname")
|
||||||
|
)
|
||||||
|
|
||||||
|
// isValidEndpoint checks that the given host / port pair are valid endpoint
|
||||||
|
func isValidEndpoint(host string, port int) bool {
|
||||||
|
return host != "" && port > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildPortsToEndpointsMap builds a map of portname -> all ip:ports for that
|
||||||
|
// portname. Explode Endpoints.Subsets[*] into this structure.
|
||||||
|
func BuildPortsToEndpointsMap(endpoints *v1.Endpoints) map[string][]string {
|
||||||
|
portsToEndpoints := map[string][]string{}
|
||||||
|
for i := range endpoints.Subsets {
|
||||||
|
ss := &endpoints.Subsets[i]
|
||||||
|
for i := range ss.Ports {
|
||||||
|
port := &ss.Ports[i]
|
||||||
|
for i := range ss.Addresses {
|
||||||
|
addr := &ss.Addresses[i]
|
||||||
|
if isValidEndpoint(addr.IP, int(port.Port)) {
|
||||||
|
portsToEndpoints[port.Name] = append(portsToEndpoints[port.Name], net.JoinHostPort(addr.IP, strconv.Itoa(int(port.Port))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return portsToEndpoints
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZeroCIDR checks whether the input CIDR string is either
|
||||||
|
// the IPv4 or IPv6 zero CIDR
|
||||||
|
func IsZeroCIDR(cidr string) bool {
|
||||||
|
if cidr == IPv4ZeroCIDR || cidr == IPv6ZeroCIDR {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsProxyableIP checks if a given IP address is permitted to be proxied
|
||||||
|
func IsProxyableIP(ip string) error {
|
||||||
|
netIP := net.ParseIP(ip)
|
||||||
|
if netIP == nil {
|
||||||
|
return ErrAddressNotAllowed
|
||||||
|
}
|
||||||
|
return isProxyableIP(netIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isProxyableIP(ip net.IP) error {
|
||||||
|
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsInterfaceLocalMulticast() {
|
||||||
|
return ErrAddressNotAllowed
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolver is an interface for net.Resolver
|
||||||
|
type Resolver interface {
|
||||||
|
LookupIPAddr(ctx context.Context, host string) ([]net.IPAddr, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsProxyableHostname checks if the IP addresses for a given hostname are permitted to be proxied
|
||||||
|
func IsProxyableHostname(ctx context.Context, resolv Resolver, hostname string) error {
|
||||||
|
resp, err := resolv.LookupIPAddr(ctx, hostname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp) == 0 {
|
||||||
|
return ErrNoAddresses
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range resp {
|
||||||
|
if err := isProxyableIP(host.IP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAllowedHost checks if the given IP host address is in a network in the denied list.
|
||||||
|
func IsAllowedHost(host net.IP, denied []*net.IPNet) error {
|
||||||
|
for _, ipNet := range denied {
|
||||||
|
if ipNet.Contains(host) {
|
||||||
|
return ErrAddressNotAllowed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLocalAddrs returns a list of all network addresses on the local system
|
||||||
|
func GetLocalAddrs() ([]net.IP, error) {
|
||||||
|
var localAddrs []net.IP
|
||||||
|
|
||||||
|
addrs, err := net.InterfaceAddrs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
ip, _, err := net.ParseCIDR(addr.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
localAddrs = append(localAddrs, ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
return localAddrs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldSkipService checks if a given service should skip proxying
|
||||||
|
func ShouldSkipService(service *v1.Service) bool {
|
||||||
|
// if ClusterIP is "None" or empty, skip proxying
|
||||||
|
if !helper.IsServiceIPSet(service) {
|
||||||
|
klog.V(3).Infof("Skipping service %s in namespace %s due to clusterIP = %q", service.Name, service.Namespace, service.Spec.ClusterIP)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Even if ClusterIP is set, ServiceTypeExternalName services don't get proxied
|
||||||
|
if service.Spec.Type == v1.ServiceTypeExternalName {
|
||||||
|
klog.V(3).Infof("Skipping service %s in namespace %s due to Type=ExternalName", service.Name, service.Namespace)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNodeAddresses return all matched node IP addresses based on given cidr slice.
|
||||||
|
// Some callers, e.g. IPVS proxier, need concrete IPs, not ranges, which is why this exists.
|
||||||
|
// NetworkInterfacer is injected for test purpose.
|
||||||
|
// We expect the cidrs passed in is already validated.
|
||||||
|
// Given an empty input `[]`, it will return `0.0.0.0/0` and `::/0` directly.
|
||||||
|
// If multiple cidrs is given, it will return the minimal IP sets, e.g. given input `[1.2.0.0/16, 0.0.0.0/0]`, it will
|
||||||
|
// only return `0.0.0.0/0`.
|
||||||
|
// NOTE: GetNodeAddresses only accepts CIDRs, if you want concrete IPs, e.g. 1.2.3.4, then the input should be 1.2.3.4/32.
|
||||||
|
func GetNodeAddresses(cidrs []string, nw NetworkInterfacer) (sets.String, error) {
|
||||||
|
uniqueAddressList := sets.NewString()
|
||||||
|
if len(cidrs) == 0 {
|
||||||
|
uniqueAddressList.Insert(IPv4ZeroCIDR)
|
||||||
|
uniqueAddressList.Insert(IPv6ZeroCIDR)
|
||||||
|
return uniqueAddressList, nil
|
||||||
|
}
|
||||||
|
// First round of iteration to pick out `0.0.0.0/0` or `::/0` for the sake of excluding non-zero IPs.
|
||||||
|
for _, cidr := range cidrs {
|
||||||
|
if IsZeroCIDR(cidr) {
|
||||||
|
uniqueAddressList.Insert(cidr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
itfs, err := nw.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error listing all interfaces from host, error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second round of iteration to parse IPs based on cidr.
|
||||||
|
for _, cidr := range cidrs {
|
||||||
|
if IsZeroCIDR(cidr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ipNet, _ := net.ParseCIDR(cidr)
|
||||||
|
for _, itf := range itfs {
|
||||||
|
addrs, err := nw.Addrs(&itf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error getting address from interface %s, error: %v", itf.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if addr == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, _, err := net.ParseCIDR(addr.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing CIDR for interface %s, error: %v", itf.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipNet.Contains(ip) {
|
||||||
|
if utilnet.IsIPv6(ip) && !uniqueAddressList.Has(IPv6ZeroCIDR) {
|
||||||
|
uniqueAddressList.Insert(ip.String())
|
||||||
|
}
|
||||||
|
if !utilnet.IsIPv6(ip) && !uniqueAddressList.Has(IPv4ZeroCIDR) {
|
||||||
|
uniqueAddressList.Insert(ip.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if uniqueAddressList.Len() == 0 {
|
||||||
|
return nil, fmt.Errorf("no addresses found for cidrs %v", cidrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return uniqueAddressList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogAndEmitIncorrectIPVersionEvent logs and emits incorrect IP version event.
|
||||||
|
func LogAndEmitIncorrectIPVersionEvent(recorder record.EventRecorder, fieldName, fieldValue, svcNamespace, svcName string, svcUID types.UID) {
|
||||||
|
errMsg := fmt.Sprintf("%s in %s has incorrect IP version", fieldValue, fieldName)
|
||||||
|
klog.Errorf("%s (service %s/%s).", errMsg, svcNamespace, svcName)
|
||||||
|
if recorder != nil {
|
||||||
|
recorder.Eventf(
|
||||||
|
&v1.ObjectReference{
|
||||||
|
Kind: "Service",
|
||||||
|
Name: svcName,
|
||||||
|
Namespace: svcNamespace,
|
||||||
|
UID: svcUID,
|
||||||
|
}, v1.EventTypeWarning, "KubeProxyIncorrectIPVersion", errMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapIPsByIPFamily maps a slice of IPs to their respective IP families (v4 or v6)
|
||||||
|
func MapIPsByIPFamily(ipStrings []string) map[v1.IPFamily][]string {
|
||||||
|
ipFamilyMap := map[v1.IPFamily][]string{}
|
||||||
|
for _, ip := range ipStrings {
|
||||||
|
// Handle only the valid IPs
|
||||||
|
if ipFamily, err := getIPFamilyFromIP(ip); err == nil {
|
||||||
|
ipFamilyMap[ipFamily] = append(ipFamilyMap[ipFamily], ip)
|
||||||
|
} else {
|
||||||
|
klog.Errorf("Skipping invalid IP: %s", ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ipFamilyMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapCIDRsByIPFamily maps a slice of IPs to their respective IP families (v4 or v6)
|
||||||
|
func MapCIDRsByIPFamily(cidrStrings []string) map[v1.IPFamily][]string {
|
||||||
|
ipFamilyMap := map[v1.IPFamily][]string{}
|
||||||
|
for _, cidr := range cidrStrings {
|
||||||
|
// Handle only the valid CIDRs
|
||||||
|
if ipFamily, err := getIPFamilyFromCIDR(cidr); err == nil {
|
||||||
|
ipFamilyMap[ipFamily] = append(ipFamilyMap[ipFamily], cidr)
|
||||||
|
} else {
|
||||||
|
klog.Errorf("Skipping invalid cidr: %s", cidr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ipFamilyMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIPFamilyFromIP(ipStr string) (v1.IPFamily, error) {
|
||||||
|
netIP := net.ParseIP(ipStr)
|
||||||
|
if netIP == nil {
|
||||||
|
return "", ErrAddressNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
if utilnet.IsIPv6(netIP) {
|
||||||
|
return v1.IPv6Protocol, nil
|
||||||
|
}
|
||||||
|
return v1.IPv4Protocol, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIPFamilyFromCIDR(cidrStr string) (v1.IPFamily, error) {
|
||||||
|
_, netCIDR, err := net.ParseCIDR(cidrStr)
|
||||||
|
if err != nil {
|
||||||
|
return "", ErrAddressNotAllowed
|
||||||
|
}
|
||||||
|
if utilnet.IsIPv6CIDR(netCIDR) {
|
||||||
|
return v1.IPv6Protocol, nil
|
||||||
|
}
|
||||||
|
return v1.IPv4Protocol, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OtherIPFamily returns the other ip family
|
||||||
|
func OtherIPFamily(ipFamily v1.IPFamily) v1.IPFamily {
|
||||||
|
if ipFamily == v1.IPv6Protocol {
|
||||||
|
return v1.IPv4Protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
return v1.IPv6Protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendPortIfNeeded appends the given port to IP address unless it is already in
|
||||||
|
// "ipv4:port" or "[ipv6]:port" format.
|
||||||
|
func AppendPortIfNeeded(addr string, port int32) string {
|
||||||
|
// Return if address is already in "ipv4:port" or "[ipv6]:port" format.
|
||||||
|
if _, _, err := net.SplitHostPort(addr); err == nil {
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simply return for invalid case. This should be caught by validation instead.
|
||||||
|
ip := net.ParseIP(addr)
|
||||||
|
if ip == nil {
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append port to address.
|
||||||
|
if ip.To4() != nil {
|
||||||
|
return fmt.Sprintf("%s:%d", addr, port)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("[%s]:%d", addr, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShuffleStrings copies strings from the specified slice into a copy in random
|
||||||
|
// order. It returns a new slice.
|
||||||
|
func ShuffleStrings(s []string) []string {
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
shuffled := make([]string, len(s))
|
||||||
|
perm := utilrand.Perm(len(s))
|
||||||
|
for i, j := range perm {
|
||||||
|
shuffled[j] = s[i]
|
||||||
|
}
|
||||||
|
return shuffled
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnsureSysctl sets a kernel sysctl to a given numeric value.
|
||||||
|
func EnsureSysctl(sysctl utilsysctl.Interface, name string, newVal int) error {
|
||||||
|
if oldVal, _ := sysctl.GetSysctl(name); oldVal != newVal {
|
||||||
|
if err := sysctl.SetSysctl(name, newVal); err != nil {
|
||||||
|
return fmt.Errorf("can't set sysctl %s to %d: %v", name, newVal, err)
|
||||||
|
}
|
||||||
|
klog.V(1).Infof("Changed sysctl %q: %d -> %d", name, oldVal, newVal)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialContext is a dial function matching the signature of net.Dialer.DialContext.
|
||||||
|
type DialContext = func(context.Context, string, string) (net.Conn, error)
|
||||||
|
|
||||||
|
// FilteredDialOptions configures how a DialContext is wrapped by NewFilteredDialContext.
|
||||||
|
type FilteredDialOptions struct {
|
||||||
|
// DialHostIPDenylist restricts hosts from being dialed.
|
||||||
|
DialHostCIDRDenylist []*net.IPNet
|
||||||
|
// AllowLocalLoopback controls connections to local loopback hosts (as defined by
|
||||||
|
// IsProxyableIP).
|
||||||
|
AllowLocalLoopback bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFilteredDialContext returns a DialContext function that filters connections based on a FilteredDialOptions.
|
||||||
|
func NewFilteredDialContext(wrapped DialContext, resolv Resolver, opts *FilteredDialOptions) DialContext {
|
||||||
|
if wrapped == nil {
|
||||||
|
wrapped = http.DefaultTransport.(*http.Transport).DialContext
|
||||||
|
}
|
||||||
|
if opts == nil {
|
||||||
|
// Do no filtering
|
||||||
|
return wrapped
|
||||||
|
}
|
||||||
|
if resolv == nil {
|
||||||
|
resolv = net.DefaultResolver
|
||||||
|
}
|
||||||
|
if len(opts.DialHostCIDRDenylist) == 0 && opts.AllowLocalLoopback {
|
||||||
|
// Do no filtering.
|
||||||
|
return wrapped
|
||||||
|
}
|
||||||
|
return func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
resp, err := resolv.LookupIPAddr(ctx, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp) == 0 {
|
||||||
|
return nil, ErrNoAddresses
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range resp {
|
||||||
|
if !opts.AllowLocalLoopback {
|
||||||
|
if err := isProxyableIP(host.IP); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.DialHostCIDRDenylist != nil {
|
||||||
|
if err := IsAllowedHost(host.IP, opts.DialHostCIDRDenylist); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wrapped(ctx, network, address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClusterIPByFamily returns a service clusterip by family
|
||||||
|
func GetClusterIPByFamily(ipFamily v1.IPFamily, service *v1.Service) string {
|
||||||
|
// allowing skew
|
||||||
|
if len(service.Spec.IPFamilies) == 0 {
|
||||||
|
if len(service.Spec.ClusterIP) == 0 || service.Spec.ClusterIP == v1.ClusterIPNone {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
IsIPv6Family := (ipFamily == v1.IPv6Protocol)
|
||||||
|
if IsIPv6Family == utilnet.IsIPv6String(service.Spec.ClusterIP) {
|
||||||
|
return service.Spec.ClusterIP
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, family := range service.Spec.IPFamilies {
|
||||||
|
if family == ipFamily {
|
||||||
|
if idx < len(service.Spec.ClusterIPs) {
|
||||||
|
return service.Spec.ClusterIPs[idx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
28
vendor/k8s.io/kubernetes/pkg/util/sysctl/BUILD
generated
vendored
Normal file
28
vendor/k8s.io/kubernetes/pkg/util/sysctl/BUILD
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
load(
|
||||||
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
|
"go_library",
|
||||||
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["sysctl.go"],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/util/sysctl",
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [
|
||||||
|
":package-srcs",
|
||||||
|
"//pkg/util/sysctl/testing:all-srcs",
|
||||||
|
],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
)
|
||||||
100
vendor/k8s.io/kubernetes/pkg/util/sysctl/sysctl.go
generated
vendored
Normal file
100
vendor/k8s.io/kubernetes/pkg/util/sysctl/sysctl.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sysctl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
sysctlBase = "/proc/sys"
|
||||||
|
// VMOvercommitMemory refers to the sysctl variable responsible for defining
|
||||||
|
// the memory over-commit policy used by kernel.
|
||||||
|
VMOvercommitMemory = "vm/overcommit_memory"
|
||||||
|
// VMPanicOnOOM refers to the sysctl variable responsible for defining
|
||||||
|
// the OOM behavior used by kernel.
|
||||||
|
VMPanicOnOOM = "vm/panic_on_oom"
|
||||||
|
// KernelPanic refers to the sysctl variable responsible for defining
|
||||||
|
// the timeout after a panic for the kernel to reboot.
|
||||||
|
KernelPanic = "kernel/panic"
|
||||||
|
// KernelPanicOnOops refers to the sysctl variable responsible for defining
|
||||||
|
// the kernel behavior when an oops or BUG is encountered.
|
||||||
|
KernelPanicOnOops = "kernel/panic_on_oops"
|
||||||
|
// RootMaxKeys refers to the sysctl variable responsible for defining
|
||||||
|
// the maximum number of keys that the root user (UID 0 in the root user namespace) may own.
|
||||||
|
RootMaxKeys = "kernel/keys/root_maxkeys"
|
||||||
|
// RootMaxBytes refers to the sysctl variable responsible for defining
|
||||||
|
// the maximum number of bytes of data that the root user (UID 0 in the root user namespace)
|
||||||
|
// can hold in the payloads of the keys owned by root.
|
||||||
|
RootMaxBytes = "kernel/keys/root_maxbytes"
|
||||||
|
|
||||||
|
// VMOvercommitMemoryAlways represents that kernel performs no memory over-commit handling.
|
||||||
|
VMOvercommitMemoryAlways = 1
|
||||||
|
// VMPanicOnOOMInvokeOOMKiller represents that kernel calls the oom_killer function when OOM occurs.
|
||||||
|
VMPanicOnOOMInvokeOOMKiller = 0
|
||||||
|
|
||||||
|
// KernelPanicOnOopsAlways represents that kernel panics on kernel oops.
|
||||||
|
KernelPanicOnOopsAlways = 1
|
||||||
|
// KernelPanicRebootTimeout is the timeout seconds after a panic for the kernel to reboot.
|
||||||
|
KernelPanicRebootTimeout = 10
|
||||||
|
|
||||||
|
// RootMaxKeysSetting is the maximum number of keys that the root user (UID 0 in the root user namespace) may own.
|
||||||
|
// Needed since docker creates a new key per container.
|
||||||
|
RootMaxKeysSetting = 1000000
|
||||||
|
// RootMaxBytesSetting is the maximum number of bytes of data that the root user (UID 0 in the root user namespace)
|
||||||
|
// can hold in the payloads of the keys owned by root.
|
||||||
|
// Allocate 25 bytes per key * number of MaxKeys.
|
||||||
|
RootMaxBytesSetting = RootMaxKeysSetting * 25
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface is an injectable interface for running sysctl commands.
|
||||||
|
type Interface interface {
|
||||||
|
// GetSysctl returns the value for the specified sysctl setting
|
||||||
|
GetSysctl(sysctl string) (int, error)
|
||||||
|
// SetSysctl modifies the specified sysctl flag to the new value
|
||||||
|
SetSysctl(sysctl string, newVal int) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Interface for accessing sysctl
|
||||||
|
func New() Interface {
|
||||||
|
return &procSysctl{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// procSysctl implements Interface by reading and writing files under /proc/sys
|
||||||
|
type procSysctl struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSysctl returns the value for the specified sysctl setting
|
||||||
|
func (*procSysctl) GetSysctl(sysctl string) (int, error) {
|
||||||
|
data, err := ioutil.ReadFile(path.Join(sysctlBase, sysctl))
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
val, err := strconv.Atoi(strings.Trim(string(data), " \n"))
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSysctl modifies the specified sysctl flag to the new value
|
||||||
|
func (*procSysctl) SetSysctl(sysctl string, newVal int) error {
|
||||||
|
return ioutil.WriteFile(path.Join(sysctlBase, sysctl), []byte(strconv.Itoa(newVal)), 0640)
|
||||||
|
}
|
||||||
134
vendor/k8s.io/kubernetes/pkg/volume/BUILD
generated
vendored
Normal file
134
vendor/k8s.io/kubernetes/pkg/volume/BUILD
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"doc.go",
|
||||||
|
"metrics_cached.go",
|
||||||
|
"metrics_du.go",
|
||||||
|
"metrics_errors.go",
|
||||||
|
"metrics_nil.go",
|
||||||
|
"metrics_statfs.go",
|
||||||
|
"noop_expandable_plugin.go",
|
||||||
|
"plugins.go",
|
||||||
|
"volume.go",
|
||||||
|
"volume_linux.go",
|
||||||
|
"volume_unsupported.go",
|
||||||
|
],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/volume",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/proxy/util:go_default_library",
|
||||||
|
"//pkg/volume/util/fs:go_default_library",
|
||||||
|
"//pkg/volume/util/hostutil:go_default_library",
|
||||||
|
"//pkg/volume/util/recyclerclient:go_default_library",
|
||||||
|
"//pkg/volume/util/subpath:go_default_library",
|
||||||
|
"//staging/src/k8s.io/api/authentication/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/informers:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/listers/storage/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
||||||
|
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||||
|
] + select({
|
||||||
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
|
"//pkg/features:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
|
"//pkg/features:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = [
|
||||||
|
"metrics_du_test.go",
|
||||||
|
"metrics_nil_test.go",
|
||||||
|
"metrics_statfs_test.go",
|
||||||
|
"plugins_test.go",
|
||||||
|
"volume_linux_test.go",
|
||||||
|
],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/volume/testing:go_default_library",
|
||||||
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/util/testing:go_default_library",
|
||||||
|
] + select({
|
||||||
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
|
"//pkg/features:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
|
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
|
||||||
|
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
|
"//pkg/features:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
|
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
|
||||||
|
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [
|
||||||
|
":package-srcs",
|
||||||
|
"//pkg/volume/awsebs:all-srcs",
|
||||||
|
"//pkg/volume/azure_file:all-srcs",
|
||||||
|
"//pkg/volume/azuredd:all-srcs",
|
||||||
|
"//pkg/volume/cephfs:all-srcs",
|
||||||
|
"//pkg/volume/cinder:all-srcs",
|
||||||
|
"//pkg/volume/configmap:all-srcs",
|
||||||
|
"//pkg/volume/csi:all-srcs",
|
||||||
|
"//pkg/volume/csimigration:all-srcs",
|
||||||
|
"//pkg/volume/downwardapi:all-srcs",
|
||||||
|
"//pkg/volume/emptydir:all-srcs",
|
||||||
|
"//pkg/volume/fc:all-srcs",
|
||||||
|
"//pkg/volume/flexvolume:all-srcs",
|
||||||
|
"//pkg/volume/flocker:all-srcs",
|
||||||
|
"//pkg/volume/gcepd:all-srcs",
|
||||||
|
"//pkg/volume/git_repo:all-srcs",
|
||||||
|
"//pkg/volume/glusterfs:all-srcs",
|
||||||
|
"//pkg/volume/hostpath:all-srcs",
|
||||||
|
"//pkg/volume/iscsi:all-srcs",
|
||||||
|
"//pkg/volume/local:all-srcs",
|
||||||
|
"//pkg/volume/nfs:all-srcs",
|
||||||
|
"//pkg/volume/portworx:all-srcs",
|
||||||
|
"//pkg/volume/projected:all-srcs",
|
||||||
|
"//pkg/volume/quobyte:all-srcs",
|
||||||
|
"//pkg/volume/rbd:all-srcs",
|
||||||
|
"//pkg/volume/scaleio:all-srcs",
|
||||||
|
"//pkg/volume/secret:all-srcs",
|
||||||
|
"//pkg/volume/storageos:all-srcs",
|
||||||
|
"//pkg/volume/testing:all-srcs",
|
||||||
|
"//pkg/volume/util:all-srcs",
|
||||||
|
"//pkg/volume/validation:all-srcs",
|
||||||
|
"//pkg/volume/vsphere_volume:all-srcs",
|
||||||
|
],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
8
vendor/k8s.io/kubernetes/pkg/volume/OWNERS
generated
vendored
Normal file
8
vendor/k8s.io/kubernetes/pkg/volume/OWNERS
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# See the OWNERS docs at https://go.k8s.io/owners
|
||||||
|
|
||||||
|
approvers:
|
||||||
|
- sig-storage-approvers
|
||||||
|
reviewers:
|
||||||
|
- sig-storage-reviewers
|
||||||
|
labels:
|
||||||
|
- sig/storage
|
||||||
19
vendor/k8s.io/kubernetes/pkg/volume/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/volume/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package volume includes internal representations of external volume types
|
||||||
|
// as well as utility methods required to mount/unmount volumes to kubelets.
|
||||||
|
package volume // import "k8s.io/kubernetes/pkg/volume"
|
||||||
74
vendor/k8s.io/kubernetes/pkg/volume/metrics_cached.go
generated
vendored
Normal file
74
vendor/k8s.io/kubernetes/pkg/volume/metrics_cached.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package volume
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ MetricsProvider = &cachedMetrics{}
|
||||||
|
|
||||||
|
// cachedMetrics represents a MetricsProvider that wraps another provider and
|
||||||
|
// caches the result.
|
||||||
|
type cachedMetrics struct {
|
||||||
|
wrapped MetricsProvider
|
||||||
|
resultError error
|
||||||
|
resultMetrics *Metrics
|
||||||
|
once cacheOnce
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCachedMetrics creates a new cachedMetrics wrapping another
|
||||||
|
// MetricsProvider and caching the results.
|
||||||
|
func NewCachedMetrics(provider MetricsProvider) MetricsProvider {
|
||||||
|
return &cachedMetrics{wrapped: provider}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetrics runs the wrapped metrics provider's GetMetrics methd once and
|
||||||
|
// caches the result. Will not cache result if there is an error.
|
||||||
|
// See MetricsProvider.GetMetrics
|
||||||
|
func (md *cachedMetrics) GetMetrics() (*Metrics, error) {
|
||||||
|
md.once.cache(func() error {
|
||||||
|
md.resultMetrics, md.resultError = md.wrapped.GetMetrics()
|
||||||
|
return md.resultError
|
||||||
|
})
|
||||||
|
return md.resultMetrics, md.resultError
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copied from sync.Once but we don't want to cache the results if there is an
|
||||||
|
// error
|
||||||
|
type cacheOnce struct {
|
||||||
|
m sync.Mutex
|
||||||
|
done uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copied from sync.Once but we don't want to cache the results if there is an
|
||||||
|
// error
|
||||||
|
func (o *cacheOnce) cache(f func() error) {
|
||||||
|
if atomic.LoadUint32(&o.done) == 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Slow-path.
|
||||||
|
o.m.Lock()
|
||||||
|
defer o.m.Unlock()
|
||||||
|
if o.done == 0 {
|
||||||
|
err := f()
|
||||||
|
if err == nil {
|
||||||
|
atomic.StoreUint32(&o.done, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
99
vendor/k8s.io/kubernetes/pkg/volume/metrics_du.go
generated
vendored
Normal file
99
vendor/k8s.io/kubernetes/pkg/volume/metrics_du.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package volume
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/util/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ MetricsProvider = &metricsDu{}
|
||||||
|
|
||||||
|
// metricsDu represents a MetricsProvider that calculates the used and
|
||||||
|
// available Volume space by calling fs.DiskUsage() and gathering
|
||||||
|
// filesystem info for the Volume path.
|
||||||
|
type metricsDu struct {
|
||||||
|
// the directory path the volume is mounted to.
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMetricsDu creates a new metricsDu with the Volume path.
|
||||||
|
func NewMetricsDu(path string) MetricsProvider {
|
||||||
|
return &metricsDu{path}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetrics calculates the volume usage and device free space by executing "du"
|
||||||
|
// and gathering filesystem info for the Volume path.
|
||||||
|
// See MetricsProvider.GetMetrics
|
||||||
|
func (md *metricsDu) GetMetrics() (*Metrics, error) {
|
||||||
|
metrics := &Metrics{Time: metav1.Now()}
|
||||||
|
if md.path == "" {
|
||||||
|
return metrics, NewNoPathDefinedError()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := md.runDiskUsage(metrics)
|
||||||
|
if err != nil {
|
||||||
|
return metrics, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = md.runFind(metrics)
|
||||||
|
if err != nil {
|
||||||
|
return metrics, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = md.getFsInfo(metrics)
|
||||||
|
if err != nil {
|
||||||
|
return metrics, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return metrics, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// runDiskUsage gets disk usage of md.path and writes the results to metrics.Used
|
||||||
|
func (md *metricsDu) runDiskUsage(metrics *Metrics) error {
|
||||||
|
used, err := fs.DiskUsage(md.path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
metrics.Used = used
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// runFind executes the "find" command and writes the results to metrics.InodesUsed
|
||||||
|
func (md *metricsDu) runFind(metrics *Metrics) error {
|
||||||
|
inodesUsed, err := fs.Find(md.path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
metrics.InodesUsed = resource.NewQuantity(inodesUsed, resource.BinarySI)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFsInfo writes metrics.Capacity and metrics.Available from the filesystem
|
||||||
|
// info
|
||||||
|
func (md *metricsDu) getFsInfo(metrics *Metrics) error {
|
||||||
|
available, capacity, _, inodes, inodesFree, _, err := fs.FsInfo(md.path)
|
||||||
|
if err != nil {
|
||||||
|
return NewFsInfoFailedError(err)
|
||||||
|
}
|
||||||
|
metrics.Available = resource.NewQuantity(available, resource.BinarySI)
|
||||||
|
metrics.Capacity = resource.NewQuantity(capacity, resource.BinarySI)
|
||||||
|
metrics.Inodes = resource.NewQuantity(inodes, resource.BinarySI)
|
||||||
|
metrics.InodesFree = resource.NewQuantity(inodesFree, resource.BinarySI)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
86
vendor/k8s.io/kubernetes/pkg/volume/metrics_errors.go
generated
vendored
Normal file
86
vendor/k8s.io/kubernetes/pkg/volume/metrics_errors.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package volume
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ErrCodeNotSupported code for NotSupported Errors.
|
||||||
|
ErrCodeNotSupported int = iota + 1
|
||||||
|
ErrCodeNoPathDefined
|
||||||
|
ErrCodeFsInfoFailed
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewNotSupportedError creates a new MetricsError with code NotSupported.
|
||||||
|
func NewNotSupportedError() *MetricsError {
|
||||||
|
return &MetricsError{
|
||||||
|
Code: ErrCodeNotSupported,
|
||||||
|
Msg: "metrics are not supported for MetricsNil Volumes",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNotSupportedErrorWithDriverName creates a new MetricsError with code NotSupported.
|
||||||
|
// driver name is added to the error message.
|
||||||
|
func NewNotSupportedErrorWithDriverName(name string) *MetricsError {
|
||||||
|
return &MetricsError{
|
||||||
|
Code: ErrCodeNotSupported,
|
||||||
|
Msg: fmt.Sprintf("metrics are not supported for %s volumes", name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNoPathDefinedError creates a new MetricsError with code NoPathDefined.
|
||||||
|
func NewNoPathDefinedError() *MetricsError {
|
||||||
|
return &MetricsError{
|
||||||
|
Code: ErrCodeNoPathDefined,
|
||||||
|
Msg: "no path defined for disk usage metrics.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFsInfoFailedError creates a new MetricsError with code FsInfoFailed.
|
||||||
|
func NewFsInfoFailedError(err error) *MetricsError {
|
||||||
|
return &MetricsError{
|
||||||
|
Code: ErrCodeFsInfoFailed,
|
||||||
|
Msg: fmt.Sprintf("failed to get FsInfo due to error %v", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetricsError to distinguish different Metrics Errors.
|
||||||
|
type MetricsError struct {
|
||||||
|
Code int
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MetricsError) Error() string {
|
||||||
|
return e.Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotSupported returns true if and only if err is "key" not found error.
|
||||||
|
func IsNotSupported(err error) bool {
|
||||||
|
return isErrCode(err, ErrCodeNotSupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isErrCode(err error, code int) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if e, ok := err.(*MetricsError); ok {
|
||||||
|
return e.Code == code
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
30
vendor/k8s.io/kubernetes/pkg/volume/metrics_nil.go
generated
vendored
Normal file
30
vendor/k8s.io/kubernetes/pkg/volume/metrics_nil.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package volume
|
||||||
|
|
||||||
|
var _ MetricsProvider = &MetricsNil{}
|
||||||
|
|
||||||
|
// MetricsNil represents a MetricsProvider that does not support returning
|
||||||
|
// Metrics. It serves as a placeholder for Volumes that do not yet support
|
||||||
|
// metrics.
|
||||||
|
type MetricsNil struct{}
|
||||||
|
|
||||||
|
// GetMetrics returns an empty Metrics and an error.
|
||||||
|
// See MetricsProvider.GetMetrics
|
||||||
|
func (*MetricsNil) GetMetrics() (*Metrics, error) {
|
||||||
|
return &Metrics{}, NewNotSupportedError()
|
||||||
|
}
|
||||||
69
vendor/k8s.io/kubernetes/pkg/volume/metrics_statfs.go
generated
vendored
Normal file
69
vendor/k8s.io/kubernetes/pkg/volume/metrics_statfs.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package volume
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/util/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ MetricsProvider = &metricsStatFS{}
|
||||||
|
|
||||||
|
// metricsStatFS represents a MetricsProvider that calculates the used and available
|
||||||
|
// Volume space by stat'ing and gathering filesystem info for the Volume path.
|
||||||
|
type metricsStatFS struct {
|
||||||
|
// the directory path the volume is mounted to.
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMetricsStatfs creates a new metricsStatFS with the Volume path.
|
||||||
|
func NewMetricsStatFS(path string) MetricsProvider {
|
||||||
|
return &metricsStatFS{path}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See MetricsProvider.GetMetrics
|
||||||
|
// GetMetrics calculates the volume usage and device free space by executing "du"
|
||||||
|
// and gathering filesystem info for the Volume path.
|
||||||
|
func (md *metricsStatFS) GetMetrics() (*Metrics, error) {
|
||||||
|
metrics := &Metrics{Time: metav1.Now()}
|
||||||
|
if md.path == "" {
|
||||||
|
return metrics, NewNoPathDefinedError()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := md.getFsInfo(metrics)
|
||||||
|
if err != nil {
|
||||||
|
return metrics, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return metrics, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFsInfo writes metrics.Capacity, metrics.Used and metrics.Available from the filesystem info
|
||||||
|
func (md *metricsStatFS) getFsInfo(metrics *Metrics) error {
|
||||||
|
available, capacity, usage, inodes, inodesFree, inodesUsed, err := fs.FsInfo(md.path)
|
||||||
|
if err != nil {
|
||||||
|
return NewFsInfoFailedError(err)
|
||||||
|
}
|
||||||
|
metrics.Available = resource.NewQuantity(available, resource.BinarySI)
|
||||||
|
metrics.Capacity = resource.NewQuantity(capacity, resource.BinarySI)
|
||||||
|
metrics.Used = resource.NewQuantity(usage, resource.BinarySI)
|
||||||
|
metrics.Inodes = resource.NewQuantity(inodes, resource.BinarySI)
|
||||||
|
metrics.InodesFree = resource.NewQuantity(inodesFree, resource.BinarySI)
|
||||||
|
metrics.InodesUsed = resource.NewQuantity(inodesUsed, resource.BinarySI)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
77
vendor/k8s.io/kubernetes/pkg/volume/noop_expandable_plugin.go
generated
vendored
Normal file
77
vendor/k8s.io/kubernetes/pkg/volume/noop_expandable_plugin.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package volume
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type noopExpandableVolumePluginInstance struct {
|
||||||
|
spec *Spec
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ExpandableVolumePlugin = &noopExpandableVolumePluginInstance{}
|
||||||
|
|
||||||
|
func (n *noopExpandableVolumePluginInstance) ExpandVolumeDevice(spec *Spec, newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error) {
|
||||||
|
return newSize, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *noopExpandableVolumePluginInstance) Init(host VolumeHost) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *noopExpandableVolumePluginInstance) GetPluginName() string {
|
||||||
|
return n.spec.KubeletExpandablePluginName()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *noopExpandableVolumePluginInstance) GetVolumeName(spec *Spec) (string, error) {
|
||||||
|
return n.spec.Name(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *noopExpandableVolumePluginInstance) CanSupport(spec *Spec) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *noopExpandableVolumePluginInstance) RequiresRemount(spec *Spec) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *noopExpandableVolumePluginInstance) NewMounter(spec *Spec, podRef *v1.Pod, opts VolumeOptions) (Mounter, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *noopExpandableVolumePluginInstance) NewUnmounter(name string, podUID types.UID) (Unmounter, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *noopExpandableVolumePluginInstance) ConstructVolumeSpec(volumeName, mountPath string) (*Spec, error) {
|
||||||
|
return n.spec, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *noopExpandableVolumePluginInstance) SupportsMountOption() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *noopExpandableVolumePluginInstance) SupportsBulkVolumeVerification() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *noopExpandableVolumePluginInstance) RequiresFSResize() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
1110
vendor/k8s.io/kubernetes/pkg/volume/plugins.go
generated
vendored
Normal file
1110
vendor/k8s.io/kubernetes/pkg/volume/plugins.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
95
vendor/k8s.io/kubernetes/pkg/volume/util/fs/BUILD
generated
vendored
Normal file
95
vendor/k8s.io/kubernetes/pkg/volume/util/fs/BUILD
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"fs.go",
|
||||||
|
"fs_unsupported.go",
|
||||||
|
"fs_windows.go",
|
||||||
|
],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/volume/util/fs",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = select({
|
||||||
|
"@io_bazel_rules_go//go/platform:aix": [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
|
"//pkg/volume/util/fsquota:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:darwin": [
|
||||||
|
"//pkg/volume/util/fsquota:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:illumos": [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:ios": [
|
||||||
|
"//pkg/volume/util/fsquota:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:js": [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
|
"//pkg/volume/util/fsquota:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:nacl": [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:plan9": [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:solaris": [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:windows": [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
"//vendor/golang.org/x/sys/windows:go_default_library",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["fs_windows_test.go"],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = select({
|
||||||
|
"@io_bazel_rules_go//go/platform:windows": [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
117
vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs.go
generated
vendored
Normal file
117
vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// +build linux darwin
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/util/fsquota"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FsInfo linux returns (available bytes, byte capacity, byte usage, total inodes, inodes free, inode usage, error)
|
||||||
|
// for the filesystem that path resides upon.
|
||||||
|
func FsInfo(path string) (int64, int64, int64, int64, int64, int64, error) {
|
||||||
|
statfs := &unix.Statfs_t{}
|
||||||
|
err := unix.Statfs(path, statfs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, 0, 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Available is blocks available * fragment size
|
||||||
|
available := int64(statfs.Bavail) * int64(statfs.Bsize)
|
||||||
|
|
||||||
|
// Capacity is total block count * fragment size
|
||||||
|
capacity := int64(statfs.Blocks) * int64(statfs.Bsize)
|
||||||
|
|
||||||
|
// Usage is block being used * fragment size (aka block size).
|
||||||
|
usage := (int64(statfs.Blocks) - int64(statfs.Bfree)) * int64(statfs.Bsize)
|
||||||
|
|
||||||
|
inodes := int64(statfs.Files)
|
||||||
|
inodesFree := int64(statfs.Ffree)
|
||||||
|
inodesUsed := inodes - inodesFree
|
||||||
|
|
||||||
|
return available, capacity, usage, inodes, inodesFree, inodesUsed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiskUsage gets disk usage of specified path.
|
||||||
|
func DiskUsage(path string) (*resource.Quantity, error) {
|
||||||
|
// First check whether the quota system knows about this directory
|
||||||
|
// A nil quantity with no error means that the path does not support quotas
|
||||||
|
// and we should use other mechanisms.
|
||||||
|
data, err := fsquota.GetConsumption(path)
|
||||||
|
if data != nil {
|
||||||
|
return data, nil
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to retrieve disk consumption via quota for %s: %v", path, err)
|
||||||
|
}
|
||||||
|
// Uses the same niceness level as cadvisor.fs does when running du
|
||||||
|
// Uses -B 1 to always scale to a blocksize of 1 byte
|
||||||
|
out, err := exec.Command("nice", "-n", "19", "du", "-x", "-s", "-B", "1", path).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed command 'du' ($ nice -n 19 du -x -s -B 1) on path %s with error %v", path, err)
|
||||||
|
}
|
||||||
|
used, err := resource.ParseQuantity(strings.Fields(string(out))[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse 'du' output %s due to error %v", out, err)
|
||||||
|
}
|
||||||
|
used.Format = resource.BinarySI
|
||||||
|
return &used, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find uses the equivalent of the command `find <path> -dev -printf '.' | wc -c` to count files and directories.
|
||||||
|
// While this is not an exact measure of inodes used, it is a very good approximation.
|
||||||
|
func Find(path string) (int64, error) {
|
||||||
|
if path == "" {
|
||||||
|
return 0, fmt.Errorf("invalid directory")
|
||||||
|
}
|
||||||
|
// First check whether the quota system knows about this directory
|
||||||
|
// A nil quantity with no error means that the path does not support quotas
|
||||||
|
// and we should use other mechanisms.
|
||||||
|
inodes, err := fsquota.GetInodes(path)
|
||||||
|
if inodes != nil {
|
||||||
|
return inodes.Value(), nil
|
||||||
|
} else if err != nil {
|
||||||
|
return 0, fmt.Errorf("unable to retrieve inode consumption via quota for %s: %v", path, err)
|
||||||
|
}
|
||||||
|
var counter byteCounter
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
findCmd := exec.Command("find", path, "-xdev", "-printf", ".")
|
||||||
|
findCmd.Stdout, findCmd.Stderr = &counter, &stderr
|
||||||
|
if err := findCmd.Start(); err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to exec cmd %v - %v; stderr: %v", findCmd.Args, err, stderr.String())
|
||||||
|
}
|
||||||
|
if err := findCmd.Wait(); err != nil {
|
||||||
|
return 0, fmt.Errorf("cmd %v failed. stderr: %s; err: %v", findCmd.Args, stderr.String(), err)
|
||||||
|
}
|
||||||
|
return counter.bytesWritten, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple io.Writer implementation that counts how many bytes were written.
|
||||||
|
type byteCounter struct{ bytesWritten int64 }
|
||||||
|
|
||||||
|
func (b *byteCounter) Write(p []byte) (int, error) {
|
||||||
|
b.bytesWritten += int64(len(p))
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
39
vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs_unsupported.go
generated
vendored
Normal file
39
vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// +build !linux,!darwin,!windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FSInfo unsupported returns 0 values for available and capacity and an error.
|
||||||
|
func FsInfo(path string) (int64, int64, int64, int64, int64, int64, error) {
|
||||||
|
return 0, 0, 0, 0, 0, 0, fmt.Errorf("FsInfo not supported for this build.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiskUsage gets disk usage of specified path.
|
||||||
|
func DiskUsage(path string) (*resource.Quantity, error) {
|
||||||
|
return nil, fmt.Errorf("Du not supported for this build.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Find(path string) (int64, error) {
|
||||||
|
return 0, fmt.Errorf("Find not supported for this build.")
|
||||||
|
}
|
||||||
121
vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs_windows.go
generated
vendored
Normal file
121
vendor/k8s.io/kubernetes/pkg/volume/util/fs/fs_windows.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
procGetDiskFreeSpaceEx = modkernel32.NewProc("GetDiskFreeSpaceExW")
|
||||||
|
)
|
||||||
|
|
||||||
|
// FSInfo returns (available bytes, byte capacity, byte usage, total inodes, inodes free, inode usage, error)
|
||||||
|
// for the filesystem that path resides upon.
|
||||||
|
func FsInfo(path string) (int64, int64, int64, int64, int64, int64, error) {
|
||||||
|
var freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes int64
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ret, _, err := syscall.Syscall6(
|
||||||
|
procGetDiskFreeSpaceEx.Addr(),
|
||||||
|
4,
|
||||||
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))),
|
||||||
|
uintptr(unsafe.Pointer(&freeBytesAvailable)),
|
||||||
|
uintptr(unsafe.Pointer(&totalNumberOfBytes)),
|
||||||
|
uintptr(unsafe.Pointer(&totalNumberOfFreeBytes)),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if ret == 0 {
|
||||||
|
return 0, 0, 0, 0, 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return freeBytesAvailable, totalNumberOfBytes, totalNumberOfBytes - freeBytesAvailable, 0, 0, 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiskUsage gets disk usage of specified path.
|
||||||
|
func DiskUsage(path string) (*resource.Quantity, error) {
|
||||||
|
info, err := os.Lstat(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
usage, err := diskUsage(path, info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
used, err := resource.ParseQuantity(fmt.Sprintf("%d", usage))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse fs usage %d due to %v", usage, err)
|
||||||
|
}
|
||||||
|
used.Format = resource.BinarySI
|
||||||
|
return &used, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always return zero since inodes is not supported on Windows.
|
||||||
|
func Find(path string) (int64, error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func diskUsage(currPath string, info os.FileInfo) (int64, error) {
|
||||||
|
var size int64
|
||||||
|
|
||||||
|
if info.Mode()&os.ModeSymlink != 0 {
|
||||||
|
return size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
size += info.Size()
|
||||||
|
|
||||||
|
if !info.IsDir() {
|
||||||
|
return size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err := os.Open(currPath)
|
||||||
|
if err != nil {
|
||||||
|
return size, err
|
||||||
|
}
|
||||||
|
defer dir.Close()
|
||||||
|
|
||||||
|
files, err := dir.Readdir(-1)
|
||||||
|
if err != nil {
|
||||||
|
return size, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
if file.IsDir() {
|
||||||
|
s, err := diskUsage(fmt.Sprintf("%s/%s", currPath, file.Name()), file)
|
||||||
|
if err != nil {
|
||||||
|
return size, err
|
||||||
|
}
|
||||||
|
size += s
|
||||||
|
} else {
|
||||||
|
size += file.Size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size, nil
|
||||||
|
}
|
||||||
78
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/BUILD
generated
vendored
Normal file
78
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/BUILD
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"project.go",
|
||||||
|
"quota.go",
|
||||||
|
"quota_linux.go",
|
||||||
|
"quota_unsupported.go",
|
||||||
|
],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/volume/util/fsquota",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/features:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
] + select({
|
||||||
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
|
"//pkg/volume/util/fsquota/common:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||||
|
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
|
"//pkg/volume/util/fsquota/common:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||||
|
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["quota_linux_test.go"],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = select({
|
||||||
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
|
"//pkg/features:go_default_library",
|
||||||
|
"//pkg/volume/util/fsquota/common:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
|
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
|
"//pkg/features:go_default_library",
|
||||||
|
"//pkg/volume/util/fsquota/common:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
|
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [
|
||||||
|
":package-srcs",
|
||||||
|
"//pkg/volume/util/fsquota/common:all-srcs",
|
||||||
|
],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
34
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/common/BUILD
generated
vendored
Normal file
34
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/common/BUILD
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"quota_linux_common.go",
|
||||||
|
"quota_linux_common_impl.go",
|
||||||
|
],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/volume/util/fsquota/common",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = select({
|
||||||
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
100
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/common/quota_linux_common.go
generated
vendored
Normal file
100
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/common/quota_linux_common.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// QuotaID is generic quota identifier.
|
||||||
|
// Data type based on quotactl(2).
|
||||||
|
type QuotaID int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UnknownQuotaID -- cannot determine whether a quota is in force
|
||||||
|
UnknownQuotaID QuotaID = -1
|
||||||
|
// BadQuotaID -- Invalid quota
|
||||||
|
BadQuotaID QuotaID = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// QuotaType -- type of quota to be applied
|
||||||
|
type QuotaType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FSQuotaAccounting for quotas for accounting only
|
||||||
|
FSQuotaAccounting QuotaType = 1 << iota
|
||||||
|
// FSQuotaEnforcing for quotas for enforcement
|
||||||
|
FSQuotaEnforcing QuotaType = 1 << iota
|
||||||
|
)
|
||||||
|
|
||||||
|
// FirstQuota is the quota ID we start with.
|
||||||
|
// XXXXXXX Need a better way of doing this...
|
||||||
|
var FirstQuota QuotaID = 1048577
|
||||||
|
|
||||||
|
// MountsFile is the location of the system mount data
|
||||||
|
var MountsFile = "/proc/self/mounts"
|
||||||
|
|
||||||
|
// MountParseRegexp parses out /proc/sys/self/mounts
|
||||||
|
var MountParseRegexp = regexp.MustCompilePOSIX("^([^ ]*)[ \t]*([^ ]*)[ \t]*([^ ]*)") // Ignore options etc.
|
||||||
|
|
||||||
|
// LinuxVolumeQuotaProvider returns an appropriate quota applier
|
||||||
|
// object if we can support quotas on this device
|
||||||
|
type LinuxVolumeQuotaProvider interface {
|
||||||
|
// GetQuotaApplier retrieves an object that can apply
|
||||||
|
// quotas (or nil if this provider cannot support quotas
|
||||||
|
// on the device)
|
||||||
|
GetQuotaApplier(mountpoint string, backingDev string) LinuxVolumeQuotaApplier
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinuxVolumeQuotaApplier is a generic interface to any quota
|
||||||
|
// mechanism supported by Linux
|
||||||
|
type LinuxVolumeQuotaApplier interface {
|
||||||
|
// GetQuotaOnDir gets the quota ID (if any) that applies to
|
||||||
|
// this directory
|
||||||
|
GetQuotaOnDir(path string) (QuotaID, error)
|
||||||
|
|
||||||
|
// SetQuotaOnDir applies the specified quota ID to a directory.
|
||||||
|
// Negative value for bytes means that a non-enforcing quota
|
||||||
|
// should be applied (perhaps by setting a quota too large to
|
||||||
|
// be hit)
|
||||||
|
SetQuotaOnDir(path string, id QuotaID, bytes int64) error
|
||||||
|
|
||||||
|
// QuotaIDIsInUse determines whether the quota ID is in use.
|
||||||
|
// Implementations should not check /etc/project or /etc/projid,
|
||||||
|
// only whether their underlying mechanism already has the ID in
|
||||||
|
// use.
|
||||||
|
// Return value of false with no error means that the ID is not
|
||||||
|
// in use; true means that it is already in use. An error
|
||||||
|
// return means that any quota ID will fail.
|
||||||
|
QuotaIDIsInUse(id QuotaID) (bool, error)
|
||||||
|
|
||||||
|
// GetConsumption returns the consumption (in bytes) of the
|
||||||
|
// directory, determined by the implementation's quota-based
|
||||||
|
// mechanism. If it is unable to do so using that mechanism,
|
||||||
|
// it should return an error and allow higher layers to
|
||||||
|
// enumerate the directory.
|
||||||
|
GetConsumption(path string, id QuotaID) (int64, error)
|
||||||
|
|
||||||
|
// GetInodes returns the number of inodes used by the
|
||||||
|
// directory, determined by the implementation's quota-based
|
||||||
|
// mechanism. If it is unable to do so using that mechanism,
|
||||||
|
// it should return an error and allow higher layers to
|
||||||
|
// enumerate the directory.
|
||||||
|
GetInodes(path string, id QuotaID) (int64, error)
|
||||||
|
}
|
||||||
286
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/common/quota_linux_common_impl.go
generated
vendored
Normal file
286
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/common/quota_linux_common_impl.go
generated
vendored
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var quotaCmd string
|
||||||
|
var quotaCmdInitialized bool
|
||||||
|
var quotaCmdLock sync.RWMutex
|
||||||
|
|
||||||
|
// If we later get a filesystem that uses project quota semantics other than
|
||||||
|
// XFS, we'll need to change this.
|
||||||
|
// Higher levels don't need to know what's inside
|
||||||
|
type linuxFilesystemType struct {
|
||||||
|
name string
|
||||||
|
typeMagic int64 // Filesystem magic number, per statfs(2)
|
||||||
|
maxQuota int64
|
||||||
|
allowEmptyOutput bool // Accept empty output from "quota" command
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
bitsPerWord = 32 << (^uint(0) >> 63) // either 32 or 64
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
linuxSupportedFilesystems = []linuxFilesystemType{
|
||||||
|
{
|
||||||
|
name: "XFS",
|
||||||
|
typeMagic: 0x58465342,
|
||||||
|
maxQuota: 1<<(bitsPerWord-1) - 1,
|
||||||
|
allowEmptyOutput: true, // XFS filesystems report nothing if a quota is not present
|
||||||
|
}, {
|
||||||
|
name: "ext4fs",
|
||||||
|
typeMagic: 0xef53,
|
||||||
|
maxQuota: (1<<(bitsPerWord-1) - 1) & (1<<58 - 1),
|
||||||
|
allowEmptyOutput: false, // ext4 filesystems always report something even if a quota is not present
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// VolumeProvider supplies a quota applier to the generic code.
|
||||||
|
type VolumeProvider struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
var quotaCmds = []string{"/sbin/xfs_quota",
|
||||||
|
"/usr/sbin/xfs_quota",
|
||||||
|
"/bin/xfs_quota"}
|
||||||
|
|
||||||
|
var quotaParseRegexp = regexp.MustCompilePOSIX("^[^ \t]*[ \t]*([0-9]+)")
|
||||||
|
|
||||||
|
var lsattrCmd = "/usr/bin/lsattr"
|
||||||
|
var lsattrParseRegexp = regexp.MustCompilePOSIX("^ *([0-9]+) [^ ]+ (.*)$")
|
||||||
|
|
||||||
|
// GetQuotaApplier -- does this backing device support quotas that
|
||||||
|
// can be applied to directories?
|
||||||
|
func (*VolumeProvider) GetQuotaApplier(mountpoint string, backingDev string) LinuxVolumeQuotaApplier {
|
||||||
|
for _, fsType := range linuxSupportedFilesystems {
|
||||||
|
if isFilesystemOfType(mountpoint, backingDev, fsType.typeMagic) {
|
||||||
|
return linuxVolumeQuotaApplier{mountpoint: mountpoint,
|
||||||
|
maxQuota: fsType.maxQuota,
|
||||||
|
allowEmptyOutput: fsType.allowEmptyOutput,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type linuxVolumeQuotaApplier struct {
|
||||||
|
mountpoint string
|
||||||
|
maxQuota int64
|
||||||
|
allowEmptyOutput bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func getXFSQuotaCmd() (string, error) {
|
||||||
|
quotaCmdLock.Lock()
|
||||||
|
defer quotaCmdLock.Unlock()
|
||||||
|
if quotaCmdInitialized {
|
||||||
|
return quotaCmd, nil
|
||||||
|
}
|
||||||
|
for _, program := range quotaCmds {
|
||||||
|
fileinfo, err := os.Stat(program)
|
||||||
|
if err == nil && ((fileinfo.Mode().Perm() & (1 << 6)) != 0) {
|
||||||
|
klog.V(3).Infof("Found xfs_quota program %s", program)
|
||||||
|
quotaCmd = program
|
||||||
|
quotaCmdInitialized = true
|
||||||
|
return quotaCmd, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quotaCmdInitialized = true
|
||||||
|
return "", fmt.Errorf("no xfs_quota program found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func doRunXFSQuotaCommand(mountpoint string, mountsFile, command string) (string, error) {
|
||||||
|
quotaCmd, err := getXFSQuotaCmd()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// We're using numeric project IDs directly; no need to scan
|
||||||
|
// /etc/projects or /etc/projid
|
||||||
|
klog.V(4).Infof("runXFSQuotaCommand %s -t %s -P/dev/null -D/dev/null -x -f %s -c %s", quotaCmd, mountsFile, mountpoint, command)
|
||||||
|
cmd := exec.Command(quotaCmd, "-t", mountsFile, "-P/dev/null", "-D/dev/null", "-x", "-f", mountpoint, "-c", command)
|
||||||
|
|
||||||
|
data, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
klog.V(4).Infof("runXFSQuotaCommand output %q", string(data))
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the mountpoint we care about into a temporary mounts file so that xfs_quota does
|
||||||
|
// not attempt to scan every mount on the filesystem, which could hang if e. g.
|
||||||
|
// a stuck NFS mount is present.
|
||||||
|
// See https://bugzilla.redhat.com/show_bug.cgi?id=237120 for an example
|
||||||
|
// of the problem that could be caused if this were to happen.
|
||||||
|
func runXFSQuotaCommand(mountpoint string, command string) (string, error) {
|
||||||
|
tmpMounts, err := ioutil.TempFile("", "mounts")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("cannot create temporary mount file: %v", err)
|
||||||
|
}
|
||||||
|
tmpMountsFileName := tmpMounts.Name()
|
||||||
|
defer tmpMounts.Close()
|
||||||
|
defer os.Remove(tmpMountsFileName)
|
||||||
|
|
||||||
|
mounts, err := os.Open(MountsFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("cannot open mounts file %s: %v", MountsFile, err)
|
||||||
|
}
|
||||||
|
defer mounts.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(mounts)
|
||||||
|
for scanner.Scan() {
|
||||||
|
match := MountParseRegexp.FindStringSubmatch(scanner.Text())
|
||||||
|
if match != nil {
|
||||||
|
mount := match[2]
|
||||||
|
if mount == mountpoint {
|
||||||
|
if _, err := tmpMounts.WriteString(fmt.Sprintf("%s\n", scanner.Text())); err != nil {
|
||||||
|
return "", fmt.Errorf("cannot write temporary mounts file: %v", err)
|
||||||
|
}
|
||||||
|
if err := tmpMounts.Sync(); err != nil {
|
||||||
|
return "", fmt.Errorf("cannot sync temporary mounts file: %v", err)
|
||||||
|
}
|
||||||
|
return doRunXFSQuotaCommand(mountpoint, tmpMountsFileName, command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("cannot run xfs_quota: cannot find mount point %s in %s", mountpoint, MountsFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsQuotas determines whether the filesystem supports quotas.
|
||||||
|
func SupportsQuotas(mountpoint string, qType QuotaType) (bool, error) {
|
||||||
|
data, err := runXFSQuotaCommand(mountpoint, "state -p")
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if qType == FSQuotaEnforcing {
|
||||||
|
return strings.Contains(data, "Enforcement: ON"), nil
|
||||||
|
}
|
||||||
|
return strings.Contains(data, "Accounting: ON"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isFilesystemOfType(mountpoint string, backingDev string, typeMagic int64) bool {
|
||||||
|
var buf syscall.Statfs_t
|
||||||
|
err := syscall.Statfs(mountpoint, &buf)
|
||||||
|
if err != nil {
|
||||||
|
klog.Warningf("Warning: Unable to statfs %s: %v", mountpoint, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if int64(buf.Type) != typeMagic {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if answer, _ := SupportsQuotas(mountpoint, FSQuotaAccounting); answer {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQuotaOnDir retrieves the quota ID (if any) associated with the specified directory
|
||||||
|
// If we can't make system calls, all we can say is that we don't know whether
|
||||||
|
// it has a quota, and higher levels have to make the call.
|
||||||
|
func (v linuxVolumeQuotaApplier) GetQuotaOnDir(path string) (QuotaID, error) {
|
||||||
|
cmd := exec.Command(lsattrCmd, "-pd", path)
|
||||||
|
data, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return BadQuotaID, fmt.Errorf("cannot run lsattr: %v", err)
|
||||||
|
}
|
||||||
|
match := lsattrParseRegexp.FindStringSubmatch(string(data))
|
||||||
|
if match == nil {
|
||||||
|
return BadQuotaID, fmt.Errorf("unable to parse lsattr -pd %s output %s", path, string(data))
|
||||||
|
}
|
||||||
|
if match[2] != path {
|
||||||
|
return BadQuotaID, fmt.Errorf("mismatch between supplied and returned path (%s != %s)", path, match[2])
|
||||||
|
}
|
||||||
|
projid, err := strconv.ParseInt(match[1], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return BadQuotaID, fmt.Errorf("unable to parse project ID from %s (%v)", match[1], err)
|
||||||
|
}
|
||||||
|
return QuotaID(projid), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetQuotaOnDir applies a quota to the specified directory under the specified mountpoint.
|
||||||
|
func (v linuxVolumeQuotaApplier) SetQuotaOnDir(path string, id QuotaID, bytes int64) error {
|
||||||
|
if bytes < 0 || bytes > v.maxQuota {
|
||||||
|
bytes = v.maxQuota
|
||||||
|
}
|
||||||
|
_, err := runXFSQuotaCommand(v.mountpoint, fmt.Sprintf("limit -p bhard=%v bsoft=%v %v", bytes, bytes, id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = runXFSQuotaCommand(v.mountpoint, fmt.Sprintf("project -s -p %s %v", path, id))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getQuantity(mountpoint string, id QuotaID, xfsQuotaArg string, multiplier int64, allowEmptyOutput bool) (int64, error) {
|
||||||
|
data, err := runXFSQuotaCommand(mountpoint, fmt.Sprintf("quota -p -N -n -v %s %v", xfsQuotaArg, id))
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("unable to run xfs_quota: %v", err)
|
||||||
|
}
|
||||||
|
if data == "" && allowEmptyOutput {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
match := quotaParseRegexp.FindStringSubmatch(data)
|
||||||
|
if match == nil {
|
||||||
|
return 0, fmt.Errorf("unable to parse quota output '%s'", data)
|
||||||
|
}
|
||||||
|
size, err := strconv.ParseInt(match[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("unable to parse data size '%s' from '%s': %v", match[1], data, err)
|
||||||
|
}
|
||||||
|
klog.V(4).Infof("getQuantity %s %d %s %d => %d %v", mountpoint, id, xfsQuotaArg, multiplier, size, err)
|
||||||
|
return size * multiplier, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConsumption returns the consumption in bytes if available via quotas
|
||||||
|
func (v linuxVolumeQuotaApplier) GetConsumption(_ string, id QuotaID) (int64, error) {
|
||||||
|
return getQuantity(v.mountpoint, id, "-b", 1024, v.allowEmptyOutput)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInodes returns the inodes in use if available via quotas
|
||||||
|
func (v linuxVolumeQuotaApplier) GetInodes(_ string, id QuotaID) (int64, error) {
|
||||||
|
return getQuantity(v.mountpoint, id, "-i", 1, v.allowEmptyOutput)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuotaIDIsInUse checks whether the specified quota ID is in use on the specified
|
||||||
|
// filesystem
|
||||||
|
func (v linuxVolumeQuotaApplier) QuotaIDIsInUse(id QuotaID) (bool, error) {
|
||||||
|
bytes, err := v.GetConsumption(v.mountpoint, id)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if bytes > 0 {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
inodes, err := v.GetInodes(v.mountpoint, id)
|
||||||
|
return inodes > 0, err
|
||||||
|
}
|
||||||
357
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/project.go
generated
vendored
Normal file
357
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/project.go
generated
vendored
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fsquota
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/util/fsquota/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
var projectsFile = "/etc/projects"
|
||||||
|
var projidFile = "/etc/projid"
|
||||||
|
|
||||||
|
var projectsParseRegexp = regexp.MustCompilePOSIX("^([[:digit:]]+):(.*)$")
|
||||||
|
var projidParseRegexp = regexp.MustCompilePOSIX("^([^#][^:]*):([[:digit:]]+)$")
|
||||||
|
|
||||||
|
var quotaIDLock sync.RWMutex
|
||||||
|
|
||||||
|
const maxUnusedQuotasToSearch = 128 // Don't go into an infinite loop searching for an unused quota
|
||||||
|
|
||||||
|
type projectType struct {
|
||||||
|
isValid bool // False if we need to remove this line
|
||||||
|
id common.QuotaID
|
||||||
|
data string // Project name (projid) or directory (projects)
|
||||||
|
line string
|
||||||
|
}
|
||||||
|
|
||||||
|
type projectsList struct {
|
||||||
|
projects []projectType
|
||||||
|
projid []projectType
|
||||||
|
}
|
||||||
|
|
||||||
|
func projFilesAreOK() error {
|
||||||
|
if sf, err := os.Lstat(projectsFile); err != nil || sf.Mode().IsRegular() {
|
||||||
|
if sf, err := os.Lstat(projidFile); err != nil || sf.Mode().IsRegular() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s exists but is not a plain file, cannot continue", projidFile)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s exists but is not a plain file, cannot continue", projectsFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lockFile(file *os.File) error {
|
||||||
|
return unix.Flock(int(file.Fd()), unix.LOCK_EX)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlockFile(file *os.File) error {
|
||||||
|
return unix.Flock(int(file.Fd()), unix.LOCK_UN)
|
||||||
|
}
|
||||||
|
|
||||||
|
// openAndLockProjectFiles opens /etc/projects and /etc/projid locked.
|
||||||
|
// Creates them if they don't exist
|
||||||
|
func openAndLockProjectFiles() (*os.File, *os.File, error) {
|
||||||
|
// Make sure neither project-related file is a symlink!
|
||||||
|
if err := projFilesAreOK(); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("system project files failed verification: %v", err)
|
||||||
|
}
|
||||||
|
// We don't actually modify the original files; we create temporaries and
|
||||||
|
// move them over the originals
|
||||||
|
fProjects, err := os.OpenFile(projectsFile, os.O_RDONLY|os.O_CREATE, 0644)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("unable to open %s: %v", projectsFile, err)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
fProjid, err := os.OpenFile(projidFile, os.O_RDONLY|os.O_CREATE, 0644)
|
||||||
|
if err == nil {
|
||||||
|
// Check once more, to ensure nothing got changed out from under us
|
||||||
|
if err = projFilesAreOK(); err == nil {
|
||||||
|
err = lockFile(fProjects)
|
||||||
|
if err == nil {
|
||||||
|
err = lockFile(fProjid)
|
||||||
|
if err == nil {
|
||||||
|
return fProjects, fProjid, nil
|
||||||
|
}
|
||||||
|
// Nothing useful we can do if we get an error here
|
||||||
|
err = fmt.Errorf("unable to lock %s: %v", projidFile, err)
|
||||||
|
unlockFile(fProjects)
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("unable to lock %s: %v", projectsFile, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("system project files failed re-verification: %v", err)
|
||||||
|
}
|
||||||
|
fProjid.Close()
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("unable to open %s: %v", projidFile, err)
|
||||||
|
}
|
||||||
|
fProjects.Close()
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeProjectFiles(fProjects *os.File, fProjid *os.File) error {
|
||||||
|
// Nothing useful we can do if either of these fail,
|
||||||
|
// but we have to close (and thereby unlock) the files anyway.
|
||||||
|
var err error
|
||||||
|
var err1 error
|
||||||
|
if fProjid != nil {
|
||||||
|
err = fProjid.Close()
|
||||||
|
}
|
||||||
|
if fProjects != nil {
|
||||||
|
err1 = fProjects.Close()
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseProject(l string) projectType {
|
||||||
|
if match := projectsParseRegexp.FindStringSubmatch(l); match != nil {
|
||||||
|
i, err := strconv.Atoi(match[1])
|
||||||
|
if err == nil {
|
||||||
|
return projectType{true, common.QuotaID(i), match[2], l}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return projectType{true, common.BadQuotaID, "", l}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseProjid(l string) projectType {
|
||||||
|
if match := projidParseRegexp.FindStringSubmatch(l); match != nil {
|
||||||
|
i, err := strconv.Atoi(match[2])
|
||||||
|
if err == nil {
|
||||||
|
return projectType{true, common.QuotaID(i), match[1], l}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return projectType{true, common.BadQuotaID, "", l}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseProjFile(f *os.File, parser func(l string) projectType) []projectType {
|
||||||
|
var answer []projectType
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
for scanner.Scan() {
|
||||||
|
answer = append(answer, parser(scanner.Text()))
|
||||||
|
}
|
||||||
|
return answer
|
||||||
|
}
|
||||||
|
|
||||||
|
func readProjectFiles(projects *os.File, projid *os.File) projectsList {
|
||||||
|
return projectsList{parseProjFile(projects, parseProject), parseProjFile(projid, parseProjid)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findAvailableQuota(path string, idMap map[common.QuotaID]bool) (common.QuotaID, error) {
|
||||||
|
unusedQuotasSearched := 0
|
||||||
|
for id := common.FirstQuota; true; id++ {
|
||||||
|
if _, ok := idMap[id]; !ok {
|
||||||
|
isInUse, err := getApplier(path).QuotaIDIsInUse(id)
|
||||||
|
if err != nil {
|
||||||
|
return common.BadQuotaID, err
|
||||||
|
} else if !isInUse {
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
unusedQuotasSearched++
|
||||||
|
if unusedQuotasSearched > maxUnusedQuotasToSearch {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return common.BadQuotaID, fmt.Errorf("cannot find available quota ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
func addDirToProject(path string, id common.QuotaID, list *projectsList) (common.QuotaID, bool, error) {
|
||||||
|
idMap := make(map[common.QuotaID]bool)
|
||||||
|
for _, project := range list.projects {
|
||||||
|
if project.data == path {
|
||||||
|
if id != project.id {
|
||||||
|
return common.BadQuotaID, false, fmt.Errorf("attempt to reassign project ID for %s", path)
|
||||||
|
}
|
||||||
|
// Trying to reassign a directory to the project it's
|
||||||
|
// already in. Maybe this should be an error, but for
|
||||||
|
// now treat it as an idempotent operation
|
||||||
|
return id, false, nil
|
||||||
|
}
|
||||||
|
idMap[project.id] = true
|
||||||
|
}
|
||||||
|
var needToAddProjid = true
|
||||||
|
for _, projid := range list.projid {
|
||||||
|
idMap[projid.id] = true
|
||||||
|
if projid.id == id && id != common.BadQuotaID {
|
||||||
|
needToAddProjid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
if id == common.BadQuotaID {
|
||||||
|
id, err = findAvailableQuota(path, idMap)
|
||||||
|
if err != nil {
|
||||||
|
return common.BadQuotaID, false, err
|
||||||
|
}
|
||||||
|
needToAddProjid = true
|
||||||
|
}
|
||||||
|
if needToAddProjid {
|
||||||
|
name := fmt.Sprintf("volume%v", id)
|
||||||
|
line := fmt.Sprintf("%s:%v", name, id)
|
||||||
|
list.projid = append(list.projid, projectType{true, id, name, line})
|
||||||
|
}
|
||||||
|
line := fmt.Sprintf("%v:%s", id, path)
|
||||||
|
list.projects = append(list.projects, projectType{true, id, path, line})
|
||||||
|
return id, needToAddProjid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeDirFromProject(path string, id common.QuotaID, list *projectsList) (bool, error) {
|
||||||
|
if id == common.BadQuotaID {
|
||||||
|
return false, fmt.Errorf("attempt to remove invalid quota ID from %s", path)
|
||||||
|
}
|
||||||
|
foundAt := -1
|
||||||
|
countByID := make(map[common.QuotaID]int)
|
||||||
|
for i, project := range list.projects {
|
||||||
|
if project.data == path {
|
||||||
|
if id != project.id {
|
||||||
|
return false, fmt.Errorf("attempting to remove quota ID %v from path %s, but expecting ID %v", id, path, project.id)
|
||||||
|
} else if foundAt != -1 {
|
||||||
|
return false, fmt.Errorf("found multiple quota IDs for path %s", path)
|
||||||
|
}
|
||||||
|
// Faster and easier than deleting an element
|
||||||
|
list.projects[i].isValid = false
|
||||||
|
foundAt = i
|
||||||
|
}
|
||||||
|
countByID[project.id]++
|
||||||
|
}
|
||||||
|
if foundAt == -1 {
|
||||||
|
return false, fmt.Errorf("cannot find quota associated with path %s", path)
|
||||||
|
}
|
||||||
|
if countByID[id] <= 1 {
|
||||||
|
// Removing the last entry means that we're no longer using
|
||||||
|
// the quota ID, so remove that as well
|
||||||
|
for i, projid := range list.projid {
|
||||||
|
if projid.id == id {
|
||||||
|
list.projid[i].isValid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeProjectFile(base *os.File, projects []projectType) (string, error) {
|
||||||
|
oname := base.Name()
|
||||||
|
stat, err := base.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
mode := stat.Mode() & os.ModePerm
|
||||||
|
f, err := ioutil.TempFile(filepath.Dir(oname), filepath.Base(oname))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
filename := f.Name()
|
||||||
|
if err := os.Chmod(filename, mode); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
for _, proj := range projects {
|
||||||
|
if proj.isValid {
|
||||||
|
if _, err := f.WriteString(fmt.Sprintf("%s\n", proj.line)); err != nil {
|
||||||
|
f.Close()
|
||||||
|
os.Remove(filename)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
os.Remove(filename)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filename, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeProjectFiles(fProjects *os.File, fProjid *os.File, writeProjid bool, list projectsList) error {
|
||||||
|
tmpProjects, err := writeProjectFile(fProjects, list.projects)
|
||||||
|
if err == nil {
|
||||||
|
// Ensure that both files are written before we try to rename either.
|
||||||
|
if writeProjid {
|
||||||
|
tmpProjid, err := writeProjectFile(fProjid, list.projid)
|
||||||
|
if err == nil {
|
||||||
|
err = os.Rename(tmpProjid, fProjid.Name())
|
||||||
|
if err != nil {
|
||||||
|
os.Remove(tmpProjid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = os.Rename(tmpProjects, fProjects.Name())
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// We're in a bit of trouble here; at this
|
||||||
|
// point we've successfully renamed tmpProjid
|
||||||
|
// to the real thing, but renaming tmpProject
|
||||||
|
// to the real file failed. There's not much we
|
||||||
|
// can do in this position. Anything we could do
|
||||||
|
// to try to undo it would itself be likely to fail.
|
||||||
|
}
|
||||||
|
os.Remove(tmpProjects)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unable to write project files: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createProjectID(path string, ID common.QuotaID) (common.QuotaID, error) {
|
||||||
|
quotaIDLock.Lock()
|
||||||
|
defer quotaIDLock.Unlock()
|
||||||
|
fProjects, fProjid, err := openAndLockProjectFiles()
|
||||||
|
if err == nil {
|
||||||
|
defer closeProjectFiles(fProjects, fProjid)
|
||||||
|
list := readProjectFiles(fProjects, fProjid)
|
||||||
|
var writeProjid bool
|
||||||
|
ID, writeProjid, err = addDirToProject(path, ID, &list)
|
||||||
|
if err == nil && ID != common.BadQuotaID {
|
||||||
|
if err = writeProjectFiles(fProjects, fProjid, writeProjid, list); err == nil {
|
||||||
|
return ID, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return common.BadQuotaID, fmt.Errorf("createProjectID %s %v failed %v", path, ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeProjectID(path string, ID common.QuotaID) error {
|
||||||
|
if ID == common.BadQuotaID {
|
||||||
|
return fmt.Errorf("attempting to remove invalid quota ID %v", ID)
|
||||||
|
}
|
||||||
|
quotaIDLock.Lock()
|
||||||
|
defer quotaIDLock.Unlock()
|
||||||
|
fProjects, fProjid, err := openAndLockProjectFiles()
|
||||||
|
if err == nil {
|
||||||
|
defer closeProjectFiles(fProjects, fProjid)
|
||||||
|
list := readProjectFiles(fProjects, fProjid)
|
||||||
|
var writeProjid bool
|
||||||
|
writeProjid, err = removeDirFromProject(path, ID, &list)
|
||||||
|
if err == nil {
|
||||||
|
if err = writeProjectFiles(fProjects, fProjid, writeProjid, list); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("removeProjectID %s %v failed %v", path, ID, err)
|
||||||
|
}
|
||||||
50
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/quota.go
generated
vendored
Normal file
50
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/quota.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fsquota
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/mount-utils"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface -- quota interface
|
||||||
|
type Interface interface {
|
||||||
|
// Does the path provided support quotas, and if so, what types
|
||||||
|
SupportsQuotas(m mount.Interface, path string) (bool, error)
|
||||||
|
// Assign a quota (picked by the quota mechanism) to a path,
|
||||||
|
// and return it.
|
||||||
|
AssignQuota(m mount.Interface, path string, poduid types.UID, bytes *resource.Quantity) error
|
||||||
|
|
||||||
|
// Get the quota-based storage consumption for the path
|
||||||
|
GetConsumption(path string) (*resource.Quantity, error)
|
||||||
|
|
||||||
|
// Get the quota-based inode consumption for the path
|
||||||
|
GetInodes(path string) (*resource.Quantity, error)
|
||||||
|
|
||||||
|
// Remove the quota from a path
|
||||||
|
// Implementations may assume that any data covered by the
|
||||||
|
// quota has already been removed.
|
||||||
|
ClearQuota(m mount.Interface, path string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func enabledQuotasForMonitoring() bool {
|
||||||
|
return utilfeature.DefaultFeatureGate.Enabled(features.LocalStorageCapacityIsolationFSQuotaMonitoring)
|
||||||
|
}
|
||||||
452
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/quota_linux.go
generated
vendored
Normal file
452
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/quota_linux.go
generated
vendored
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fsquota
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
"k8s.io/mount-utils"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/util/uuid"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/util/fsquota/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Pod -> ID
|
||||||
|
var podQuotaMap = make(map[types.UID]common.QuotaID)
|
||||||
|
|
||||||
|
// Dir -> ID (for convenience)
|
||||||
|
var dirQuotaMap = make(map[string]common.QuotaID)
|
||||||
|
|
||||||
|
// ID -> pod
|
||||||
|
var quotaPodMap = make(map[common.QuotaID]types.UID)
|
||||||
|
|
||||||
|
// Directory -> pod
|
||||||
|
var dirPodMap = make(map[string]types.UID)
|
||||||
|
|
||||||
|
// Backing device -> applier
|
||||||
|
// This is *not* cleaned up; its size will be bounded.
|
||||||
|
var devApplierMap = make(map[string]common.LinuxVolumeQuotaApplier)
|
||||||
|
|
||||||
|
// Directory -> applier
|
||||||
|
var dirApplierMap = make(map[string]common.LinuxVolumeQuotaApplier)
|
||||||
|
var dirApplierLock sync.RWMutex
|
||||||
|
|
||||||
|
// Pod -> refcount
|
||||||
|
var podDirCountMap = make(map[types.UID]int)
|
||||||
|
|
||||||
|
// ID -> size
|
||||||
|
var quotaSizeMap = make(map[common.QuotaID]int64)
|
||||||
|
var quotaLock sync.RWMutex
|
||||||
|
|
||||||
|
var supportsQuotasMap = make(map[string]bool)
|
||||||
|
var supportsQuotasLock sync.RWMutex
|
||||||
|
|
||||||
|
// Directory -> backingDev
|
||||||
|
var backingDevMap = make(map[string]string)
|
||||||
|
var backingDevLock sync.RWMutex
|
||||||
|
|
||||||
|
var mountpointMap = make(map[string]string)
|
||||||
|
var mountpointLock sync.RWMutex
|
||||||
|
|
||||||
|
var providers = []common.LinuxVolumeQuotaProvider{
|
||||||
|
&common.VolumeProvider{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Separate the innards for ease of testing
|
||||||
|
func detectBackingDevInternal(mountpoint string, mounts string) (string, error) {
|
||||||
|
file, err := os.Open(mounts)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
match := common.MountParseRegexp.FindStringSubmatch(scanner.Text())
|
||||||
|
if match != nil {
|
||||||
|
device := match[1]
|
||||||
|
mount := match[2]
|
||||||
|
if mount == mountpoint {
|
||||||
|
return device, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("couldn't find backing device for %s", mountpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
// detectBackingDev assumes that the mount point provided is valid
|
||||||
|
func detectBackingDev(_ mount.Interface, mountpoint string) (string, error) {
|
||||||
|
return detectBackingDevInternal(mountpoint, common.MountsFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearBackingDev(path string) {
|
||||||
|
backingDevLock.Lock()
|
||||||
|
defer backingDevLock.Unlock()
|
||||||
|
delete(backingDevMap, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assumes that the path has been fully canonicalized
|
||||||
|
// Breaking this up helps with testing
|
||||||
|
func detectMountpointInternal(m mount.Interface, path string) (string, error) {
|
||||||
|
for path != "" && path != "/" {
|
||||||
|
// per k8s.io/mount-utils/mount_linux this detects all but
|
||||||
|
// a bind mount from one part of a mount to another.
|
||||||
|
// For our purposes that's fine; we simply want the "true"
|
||||||
|
// mount point
|
||||||
|
//
|
||||||
|
// IsNotMountPoint proved much more troublesome; it actually
|
||||||
|
// scans the mounts, and when a lot of mount/unmount
|
||||||
|
// activity takes place, it is not able to get a consistent
|
||||||
|
// view of /proc/self/mounts, causing it to time out and
|
||||||
|
// report incorrectly.
|
||||||
|
isNotMount, err := m.IsLikelyNotMountPoint(path)
|
||||||
|
if err != nil {
|
||||||
|
return "/", err
|
||||||
|
}
|
||||||
|
if !isNotMount {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
path = filepath.Dir(path)
|
||||||
|
}
|
||||||
|
return "/", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectMountpoint(m mount.Interface, path string) (string, error) {
|
||||||
|
xpath, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return "/", err
|
||||||
|
}
|
||||||
|
xpath, err = filepath.EvalSymlinks(xpath)
|
||||||
|
if err != nil {
|
||||||
|
return "/", err
|
||||||
|
}
|
||||||
|
if xpath, err = detectMountpointInternal(m, xpath); err == nil {
|
||||||
|
return xpath, nil
|
||||||
|
}
|
||||||
|
return "/", err
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearMountpoint(path string) {
|
||||||
|
mountpointLock.Lock()
|
||||||
|
defer mountpointLock.Unlock()
|
||||||
|
delete(mountpointMap, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFSInfo Returns mountpoint and backing device
|
||||||
|
// getFSInfo should cache the mountpoint and backing device for the
|
||||||
|
// path.
|
||||||
|
func getFSInfo(m mount.Interface, path string) (string, string, error) {
|
||||||
|
mountpointLock.Lock()
|
||||||
|
defer mountpointLock.Unlock()
|
||||||
|
|
||||||
|
backingDevLock.Lock()
|
||||||
|
defer backingDevLock.Unlock()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
mountpoint, okMountpoint := mountpointMap[path]
|
||||||
|
if !okMountpoint {
|
||||||
|
mountpoint, err = detectMountpoint(m, path)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("cannot determine mountpoint for %s: %v", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backingDev, okBackingDev := backingDevMap[path]
|
||||||
|
if !okBackingDev {
|
||||||
|
backingDev, err = detectBackingDev(m, mountpoint)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("cannot determine backing device for %s: %v", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mountpointMap[path] = mountpoint
|
||||||
|
backingDevMap[path] = backingDev
|
||||||
|
return mountpoint, backingDev, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearFSInfo(path string) {
|
||||||
|
clearMountpoint(path)
|
||||||
|
clearBackingDev(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getApplier(path string) common.LinuxVolumeQuotaApplier {
|
||||||
|
dirApplierLock.Lock()
|
||||||
|
defer dirApplierLock.Unlock()
|
||||||
|
return dirApplierMap[path]
|
||||||
|
}
|
||||||
|
|
||||||
|
func setApplier(path string, applier common.LinuxVolumeQuotaApplier) {
|
||||||
|
dirApplierLock.Lock()
|
||||||
|
defer dirApplierLock.Unlock()
|
||||||
|
dirApplierMap[path] = applier
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearApplier(path string) {
|
||||||
|
dirApplierLock.Lock()
|
||||||
|
defer dirApplierLock.Unlock()
|
||||||
|
delete(dirApplierMap, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setQuotaOnDir(path string, id common.QuotaID, bytes int64) error {
|
||||||
|
return getApplier(path).SetQuotaOnDir(path, id, bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getQuotaOnDir(m mount.Interface, path string) (common.QuotaID, error) {
|
||||||
|
_, _, err := getFSInfo(m, path)
|
||||||
|
if err != nil {
|
||||||
|
return common.BadQuotaID, err
|
||||||
|
}
|
||||||
|
return getApplier(path).GetQuotaOnDir(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearQuotaOnDir(m mount.Interface, path string) error {
|
||||||
|
// Since we may be called without path being in the map,
|
||||||
|
// we explicitly have to check in this case.
|
||||||
|
klog.V(4).Infof("clearQuotaOnDir %s", path)
|
||||||
|
supportsQuotas, err := SupportsQuotas(m, path)
|
||||||
|
if err != nil {
|
||||||
|
// Log-and-continue instead of returning an error for now
|
||||||
|
// due to unspecified backwards compatibility concerns (a subject to revise)
|
||||||
|
klog.V(3).Infof("Attempt to check for quota support failed: %v", err)
|
||||||
|
}
|
||||||
|
if !supportsQuotas {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
projid, err := getQuotaOnDir(m, path)
|
||||||
|
if err == nil && projid != common.BadQuotaID {
|
||||||
|
// This means that we have a quota on the directory but
|
||||||
|
// we can't clear it. That's not good.
|
||||||
|
err = setQuotaOnDir(path, projid, 0)
|
||||||
|
if err != nil {
|
||||||
|
klog.V(3).Infof("Attempt to clear quota failed: %v", err)
|
||||||
|
}
|
||||||
|
// Even if clearing the quota failed, we still need to
|
||||||
|
// try to remove the project ID, or that may be left dangling.
|
||||||
|
err1 := removeProjectID(path, projid)
|
||||||
|
if err1 != nil {
|
||||||
|
klog.V(3).Infof("Attempt to remove quota ID from system files failed: %v", err1)
|
||||||
|
}
|
||||||
|
clearFSInfo(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
// If we couldn't get a quota, that's fine -- there may
|
||||||
|
// never have been one, and we have no way to know otherwise
|
||||||
|
klog.V(3).Infof("clearQuotaOnDir fails %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsQuotas -- Does the path support quotas
|
||||||
|
// Cache the applier for paths that support quotas. For paths that don't,
|
||||||
|
// don't cache the result because nothing will clean it up.
|
||||||
|
// However, do cache the device->applier map; the number of devices
|
||||||
|
// is bounded.
|
||||||
|
func SupportsQuotas(m mount.Interface, path string) (bool, error) {
|
||||||
|
if !enabledQuotasForMonitoring() {
|
||||||
|
klog.V(3).Info("SupportsQuotas called, but quotas disabled")
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
supportsQuotasLock.Lock()
|
||||||
|
defer supportsQuotasLock.Unlock()
|
||||||
|
if supportsQuotas, ok := supportsQuotasMap[path]; ok {
|
||||||
|
return supportsQuotas, nil
|
||||||
|
}
|
||||||
|
mount, dev, err := getFSInfo(m, path)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// Do we know about this device?
|
||||||
|
applier, ok := devApplierMap[mount]
|
||||||
|
if !ok {
|
||||||
|
for _, provider := range providers {
|
||||||
|
if applier = provider.GetQuotaApplier(mount, dev); applier != nil {
|
||||||
|
devApplierMap[mount] = applier
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if applier != nil {
|
||||||
|
supportsQuotasMap[path] = true
|
||||||
|
setApplier(path, applier)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
delete(backingDevMap, path)
|
||||||
|
delete(mountpointMap, path)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssignQuota -- assign a quota to the specified directory.
|
||||||
|
// AssignQuota chooses the quota ID based on the pod UID and path.
|
||||||
|
// If the pod UID is identical to another one known, it may (but presently
|
||||||
|
// doesn't) choose the same quota ID as other volumes in the pod.
|
||||||
|
//lint:ignore SA4009 poduid is overwritten by design, see comment below
|
||||||
|
func AssignQuota(m mount.Interface, path string, poduid types.UID, bytes *resource.Quantity) error {
|
||||||
|
if bytes == nil {
|
||||||
|
return fmt.Errorf("attempting to assign null quota to %s", path)
|
||||||
|
}
|
||||||
|
ibytes := bytes.Value()
|
||||||
|
if ok, err := SupportsQuotas(m, path); !ok {
|
||||||
|
return fmt.Errorf("quotas not supported on %s: %v", path, err)
|
||||||
|
}
|
||||||
|
quotaLock.Lock()
|
||||||
|
defer quotaLock.Unlock()
|
||||||
|
// Current policy is to set individual quotas on each volumes.
|
||||||
|
// If we decide later that we want to assign one quota for all
|
||||||
|
// volumes in a pod, we can simply remove this line of code.
|
||||||
|
// If and when we decide permanently that we're going to adopt
|
||||||
|
// one quota per volume, we can rip all of the pod code out.
|
||||||
|
poduid = types.UID(uuid.NewUUID())
|
||||||
|
if pod, ok := dirPodMap[path]; ok && pod != poduid {
|
||||||
|
return fmt.Errorf("requesting quota on existing directory %s but different pod %s %s", path, pod, poduid)
|
||||||
|
}
|
||||||
|
oid, ok := podQuotaMap[poduid]
|
||||||
|
if ok {
|
||||||
|
if quotaSizeMap[oid] != ibytes {
|
||||||
|
return fmt.Errorf("requesting quota of different size: old %v new %v", quotaSizeMap[oid], bytes)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
oid = common.BadQuotaID
|
||||||
|
}
|
||||||
|
id, err := createProjectID(path, oid)
|
||||||
|
if err == nil {
|
||||||
|
if oid != common.BadQuotaID && oid != id {
|
||||||
|
return fmt.Errorf("attempt to reassign quota %v to %v", oid, id)
|
||||||
|
}
|
||||||
|
// When enforcing quotas are enabled, we'll condition this
|
||||||
|
// on their being disabled also.
|
||||||
|
if ibytes > 0 {
|
||||||
|
ibytes = -1
|
||||||
|
}
|
||||||
|
if err = setQuotaOnDir(path, id, ibytes); err == nil {
|
||||||
|
quotaPodMap[id] = poduid
|
||||||
|
quotaSizeMap[id] = ibytes
|
||||||
|
podQuotaMap[poduid] = id
|
||||||
|
dirQuotaMap[path] = id
|
||||||
|
dirPodMap[path] = poduid
|
||||||
|
podDirCountMap[poduid]++
|
||||||
|
klog.V(4).Infof("Assigning quota ID %d (%d) to %s", id, ibytes, path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
removeProjectID(path, id)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("assign quota FAILED %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConsumption -- retrieve the consumption (in bytes) of the directory
|
||||||
|
func GetConsumption(path string) (*resource.Quantity, error) {
|
||||||
|
// Note that we actually need to hold the lock at least through
|
||||||
|
// running the quota command, so it can't get recycled behind our back
|
||||||
|
quotaLock.Lock()
|
||||||
|
defer quotaLock.Unlock()
|
||||||
|
applier := getApplier(path)
|
||||||
|
// No applier means directory is not under quota management
|
||||||
|
if applier == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
ibytes, err := applier.GetConsumption(path, dirQuotaMap[path])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resource.NewQuantity(ibytes, resource.DecimalSI), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInodes -- retrieve the number of inodes in use under the directory
|
||||||
|
func GetInodes(path string) (*resource.Quantity, error) {
|
||||||
|
// Note that we actually need to hold the lock at least through
|
||||||
|
// running the quota command, so it can't get recycled behind our back
|
||||||
|
quotaLock.Lock()
|
||||||
|
defer quotaLock.Unlock()
|
||||||
|
applier := getApplier(path)
|
||||||
|
// No applier means directory is not under quota management
|
||||||
|
if applier == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
inodes, err := applier.GetInodes(path, dirQuotaMap[path])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resource.NewQuantity(inodes, resource.DecimalSI), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearQuota -- remove the quota assigned to a directory
|
||||||
|
func ClearQuota(m mount.Interface, path string) error {
|
||||||
|
klog.V(3).Infof("ClearQuota %s", path)
|
||||||
|
if !enabledQuotasForMonitoring() {
|
||||||
|
return fmt.Errorf("clearQuota called, but quotas disabled")
|
||||||
|
}
|
||||||
|
quotaLock.Lock()
|
||||||
|
defer quotaLock.Unlock()
|
||||||
|
poduid, ok := dirPodMap[path]
|
||||||
|
if !ok {
|
||||||
|
// Nothing in the map either means that there was no
|
||||||
|
// quota to begin with or that we're clearing a
|
||||||
|
// stale directory, so if we find a quota, just remove it.
|
||||||
|
// The process of clearing the quota requires that an applier
|
||||||
|
// be found, which needs to be cleaned up.
|
||||||
|
defer delete(supportsQuotasMap, path)
|
||||||
|
defer clearApplier(path)
|
||||||
|
return clearQuotaOnDir(m, path)
|
||||||
|
}
|
||||||
|
_, ok = podQuotaMap[poduid]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("clearQuota: No quota available for %s", path)
|
||||||
|
}
|
||||||
|
projid, err := getQuotaOnDir(m, path)
|
||||||
|
if err != nil {
|
||||||
|
// Log-and-continue instead of returning an error for now
|
||||||
|
// due to unspecified backwards compatibility concerns (a subject to revise)
|
||||||
|
klog.V(3).Infof("Attempt to check quota ID %v on dir %s failed: %v", dirQuotaMap[path], path, err)
|
||||||
|
}
|
||||||
|
if projid != dirQuotaMap[path] {
|
||||||
|
return fmt.Errorf("expected quota ID %v on dir %s does not match actual %v", dirQuotaMap[path], path, projid)
|
||||||
|
}
|
||||||
|
count, ok := podDirCountMap[poduid]
|
||||||
|
if count <= 1 || !ok {
|
||||||
|
err = clearQuotaOnDir(m, path)
|
||||||
|
// This error should be noted; we still need to clean up
|
||||||
|
// and otherwise handle in the same way.
|
||||||
|
if err != nil {
|
||||||
|
klog.V(3).Infof("Unable to clear quota %v %s: %v", dirQuotaMap[path], path, err)
|
||||||
|
}
|
||||||
|
delete(quotaSizeMap, podQuotaMap[poduid])
|
||||||
|
delete(quotaPodMap, podQuotaMap[poduid])
|
||||||
|
delete(podDirCountMap, poduid)
|
||||||
|
delete(podQuotaMap, poduid)
|
||||||
|
} else {
|
||||||
|
err = removeProjectID(path, projid)
|
||||||
|
podDirCountMap[poduid]--
|
||||||
|
klog.V(4).Infof("Not clearing quota for pod %s; still %v dirs outstanding", poduid, podDirCountMap[poduid])
|
||||||
|
}
|
||||||
|
delete(dirPodMap, path)
|
||||||
|
delete(dirQuotaMap, path)
|
||||||
|
delete(supportsQuotasMap, path)
|
||||||
|
clearApplier(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to clear quota for %s: %v", path, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
58
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/quota_unsupported.go
generated
vendored
Normal file
58
vendor/k8s.io/kubernetes/pkg/volume/util/fsquota/quota_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// +build !linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fsquota
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"k8s.io/mount-utils"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Dummy quota implementation for systems that do not implement support
|
||||||
|
// for volume quotas
|
||||||
|
|
||||||
|
var errNotImplemented = errors.New("not implemented")
|
||||||
|
|
||||||
|
// SupportsQuotas -- dummy implementation
|
||||||
|
func SupportsQuotas(_ mount.Interface, _ string) (bool, error) {
|
||||||
|
return false, errNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssignQuota -- dummy implementation
|
||||||
|
func AssignQuota(_ mount.Interface, _ string, _ types.UID, _ *resource.Quantity) error {
|
||||||
|
return errNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConsumption -- dummy implementation
|
||||||
|
func GetConsumption(_ string) (*resource.Quantity, error) {
|
||||||
|
return nil, errNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInodes -- dummy implementation
|
||||||
|
func GetInodes(_ string) (*resource.Quantity, error) {
|
||||||
|
return nil, errNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearQuota -- dummy implementation
|
||||||
|
func ClearQuota(_ mount.Interface, _ string) error {
|
||||||
|
return errNotImplemented
|
||||||
|
}
|
||||||
65
vendor/k8s.io/kubernetes/pkg/volume/util/hostutil/BUILD
generated
vendored
Normal file
65
vendor/k8s.io/kubernetes/pkg/volume/util/hostutil/BUILD
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"fake_hostutil.go",
|
||||||
|
"hostutil.go",
|
||||||
|
"hostutil_linux.go",
|
||||||
|
"hostutil_unsupported.go",
|
||||||
|
"hostutil_windows.go",
|
||||||
|
],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/volume/util/hostutil",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
] + select({
|
||||||
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
|
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/path:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
|
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/path:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:windows": [
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/path:go_default_library",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = [
|
||||||
|
"hostutil_linux_test.go",
|
||||||
|
"hostutil_windows_test.go",
|
||||||
|
],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = select({
|
||||||
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
|
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
|
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
118
vendor/k8s.io/kubernetes/pkg/volume/util/hostutil/fake_hostutil.go
generated
vendored
Normal file
118
vendor/k8s.io/kubernetes/pkg/volume/util/hostutil/fake_hostutil.go
generated
vendored
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package hostutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"k8s.io/mount-utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FakeHostUtil is a fake HostUtils implementation for testing
|
||||||
|
type FakeHostUtil struct {
|
||||||
|
MountPoints []mount.MountPoint
|
||||||
|
Filesystem map[string]FileType
|
||||||
|
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFakeHostUtil returns a struct that implements the HostUtils interface
|
||||||
|
// for testing
|
||||||
|
// TODO: no callers were initializing the struct with any MountPoints. Check
|
||||||
|
// if those are still being used by any callers and if MountPoints still need
|
||||||
|
// to be a part of the struct.
|
||||||
|
func NewFakeHostUtil(fs map[string]FileType) *FakeHostUtil {
|
||||||
|
return &FakeHostUtil{
|
||||||
|
Filesystem: fs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile-time check to make sure FakeHostUtil implements interface
|
||||||
|
var _ HostUtils = &FakeHostUtil{}
|
||||||
|
|
||||||
|
// DeviceOpened checks if block device referenced by pathname is in use by
|
||||||
|
// checking if is listed as a device in the in-memory mountpoint table.
|
||||||
|
func (hu *FakeHostUtil) DeviceOpened(pathname string) (bool, error) {
|
||||||
|
hu.mutex.Lock()
|
||||||
|
defer hu.mutex.Unlock()
|
||||||
|
|
||||||
|
for _, mp := range hu.MountPoints {
|
||||||
|
if mp.Device == pathname {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathIsDevice always returns true
|
||||||
|
func (hu *FakeHostUtil) PathIsDevice(pathname string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDeviceNameFromMount given a mount point, find the volume id
|
||||||
|
func (hu *FakeHostUtil) GetDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error) {
|
||||||
|
return getDeviceNameFromMount(mounter, mountPath, pluginMountDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRShared checks if path is shared and bind-mounts it as rshared if needed.
|
||||||
|
// No-op for testing
|
||||||
|
func (hu *FakeHostUtil) MakeRShared(path string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileType checks for file/directory/socket/block/character devices.
|
||||||
|
// Defaults to Directory if otherwise unspecified.
|
||||||
|
func (hu *FakeHostUtil) GetFileType(pathname string) (FileType, error) {
|
||||||
|
if t, ok := hu.Filesystem[pathname]; ok {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
return FileType("Directory"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathExists checks if pathname exists.
|
||||||
|
func (hu *FakeHostUtil) PathExists(pathname string) (bool, error) {
|
||||||
|
if _, ok := hu.Filesystem[pathname]; ok {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvalHostSymlinks returns the path name after evaluating symlinks.
|
||||||
|
// No-op for testing
|
||||||
|
func (hu *FakeHostUtil) EvalHostSymlinks(pathname string) (string, error) {
|
||||||
|
return pathname, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOwner returns the integer ID for the user and group of the given path
|
||||||
|
// Not implemented for testing
|
||||||
|
func (hu *FakeHostUtil) GetOwner(pathname string) (int64, int64, error) {
|
||||||
|
return -1, -1, errors.New("GetOwner not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSELinuxSupport tests if pathname is on a mount that supports SELinux.
|
||||||
|
// Not implemented for testing
|
||||||
|
func (hu *FakeHostUtil) GetSELinuxSupport(pathname string) (bool, error) {
|
||||||
|
return false, errors.New("GetSELinuxSupport not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMode returns permissions of pathname.
|
||||||
|
// Not implemented for testing
|
||||||
|
func (hu *FakeHostUtil) GetMode(pathname string) (os.FileMode, error) {
|
||||||
|
return 0, errors.New("not implemented")
|
||||||
|
}
|
||||||
109
vendor/k8s.io/kubernetes/pkg/volume/util/hostutil/hostutil.go
generated
vendored
Normal file
109
vendor/k8s.io/kubernetes/pkg/volume/util/hostutil/hostutil.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package hostutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"k8s.io/mount-utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileType enumerates the known set of possible file types.
|
||||||
|
type FileType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FileTypeBlockDev defines a constant for the block device FileType.
|
||||||
|
FileTypeBlockDev FileType = "BlockDevice"
|
||||||
|
// FileTypeCharDev defines a constant for the character device FileType.
|
||||||
|
FileTypeCharDev FileType = "CharDevice"
|
||||||
|
// FileTypeDirectory defines a constant for the directory FileType.
|
||||||
|
FileTypeDirectory FileType = "Directory"
|
||||||
|
// FileTypeFile defines a constant for the file FileType.
|
||||||
|
FileTypeFile FileType = "File"
|
||||||
|
// FileTypeSocket defines a constant for the socket FileType.
|
||||||
|
FileTypeSocket FileType = "Socket"
|
||||||
|
// FileTypeUnknown defines a constant for an unknown FileType.
|
||||||
|
FileTypeUnknown FileType = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
// HostUtils defines the set of methods for interacting with paths on a host.
|
||||||
|
type HostUtils interface {
|
||||||
|
// DeviceOpened determines if the device (e.g. /dev/sdc) is in use elsewhere
|
||||||
|
// on the system, i.e. still mounted.
|
||||||
|
DeviceOpened(pathname string) (bool, error)
|
||||||
|
// PathIsDevice determines if a path is a device.
|
||||||
|
PathIsDevice(pathname string) (bool, error)
|
||||||
|
// GetDeviceNameFromMount finds the device name by checking the mount path
|
||||||
|
// to get the global mount path within its plugin directory.
|
||||||
|
GetDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error)
|
||||||
|
// MakeRShared checks that given path is on a mount with 'rshared' mount
|
||||||
|
// propagation. If not, it bind-mounts the path as rshared.
|
||||||
|
MakeRShared(path string) error
|
||||||
|
// GetFileType checks for file/directory/socket/block/character devices.
|
||||||
|
GetFileType(pathname string) (FileType, error)
|
||||||
|
// PathExists tests if the given path already exists
|
||||||
|
// Error is returned on any other error than "file not found".
|
||||||
|
PathExists(pathname string) (bool, error)
|
||||||
|
// EvalHostSymlinks returns the path name after evaluating symlinks.
|
||||||
|
EvalHostSymlinks(pathname string) (string, error)
|
||||||
|
// GetOwner returns the integer ID for the user and group of the given path
|
||||||
|
GetOwner(pathname string) (int64, int64, error)
|
||||||
|
// GetSELinuxSupport returns true if given path is on a mount that supports
|
||||||
|
// SELinux.
|
||||||
|
GetSELinuxSupport(pathname string) (bool, error)
|
||||||
|
// GetMode returns permissions of the path.
|
||||||
|
GetMode(pathname string) (os.FileMode, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile-time check to ensure all HostUtil implementations satisfy
|
||||||
|
// the Interface.
|
||||||
|
var _ HostUtils = &HostUtil{}
|
||||||
|
|
||||||
|
// getFileType checks for file/directory/socket and block/character devices.
|
||||||
|
func getFileType(pathname string) (FileType, error) {
|
||||||
|
var pathType FileType
|
||||||
|
info, err := os.Stat(pathname)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return pathType, fmt.Errorf("path %q does not exist", pathname)
|
||||||
|
}
|
||||||
|
// err in call to os.Stat
|
||||||
|
if err != nil {
|
||||||
|
return pathType, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks whether the mode is the target mode.
|
||||||
|
isSpecificMode := func(mode, targetMode os.FileMode) bool {
|
||||||
|
return mode&targetMode == targetMode
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := info.Mode()
|
||||||
|
if mode.IsDir() {
|
||||||
|
return FileTypeDirectory, nil
|
||||||
|
} else if mode.IsRegular() {
|
||||||
|
return FileTypeFile, nil
|
||||||
|
} else if isSpecificMode(mode, os.ModeSocket) {
|
||||||
|
return FileTypeSocket, nil
|
||||||
|
} else if isSpecificMode(mode, os.ModeDevice) {
|
||||||
|
if isSpecificMode(mode, os.ModeCharDevice) {
|
||||||
|
return FileTypeCharDev, nil
|
||||||
|
}
|
||||||
|
return FileTypeBlockDev, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
|
||||||
|
}
|
||||||
291
vendor/k8s.io/kubernetes/pkg/volume/util/hostutil/hostutil_linux.go
generated
vendored
Normal file
291
vendor/k8s.io/kubernetes/pkg/volume/util/hostutil/hostutil_linux.go
generated
vendored
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package hostutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
"k8s.io/mount-utils"
|
||||||
|
utilpath "k8s.io/utils/path"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Location of the mountinfo file
|
||||||
|
procMountInfoPath = "/proc/self/mountinfo"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HostUtil implements HostUtils for Linux platforms.
|
||||||
|
type HostUtil struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHostUtil returns a struct that implements the HostUtils interface on
|
||||||
|
// linux platforms
|
||||||
|
func NewHostUtil() *HostUtil {
|
||||||
|
return &HostUtil{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
|
||||||
|
// If pathname is not a device, log and return false with nil error.
|
||||||
|
// If open returns errno EBUSY, return true with nil error.
|
||||||
|
// If open returns nil, return false with nil error.
|
||||||
|
// Otherwise, return false with error
|
||||||
|
func (hu *HostUtil) DeviceOpened(pathname string) (bool, error) {
|
||||||
|
return ExclusiveOpenFailsOnDevice(pathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
|
||||||
|
// to a device.
|
||||||
|
func (hu *HostUtil) PathIsDevice(pathname string) (bool, error) {
|
||||||
|
pathType, err := hu.GetFileType(pathname)
|
||||||
|
isDevice := pathType == FileTypeCharDev || pathType == FileTypeBlockDev
|
||||||
|
return isDevice, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExclusiveOpenFailsOnDevice is shared with NsEnterMounter
|
||||||
|
func ExclusiveOpenFailsOnDevice(pathname string) (bool, error) {
|
||||||
|
var isDevice bool
|
||||||
|
finfo, err := os.Stat(pathname)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
isDevice = false
|
||||||
|
}
|
||||||
|
// err in call to os.Stat
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf(
|
||||||
|
"PathIsDevice failed for path %q: %v",
|
||||||
|
pathname,
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
// path refers to a device
|
||||||
|
if finfo.Mode()&os.ModeDevice != 0 {
|
||||||
|
isDevice = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isDevice {
|
||||||
|
klog.Errorf("Path %q is not referring to a device.", pathname)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
fd, errno := unix.Open(pathname, unix.O_RDONLY|unix.O_EXCL|unix.O_CLOEXEC, 0)
|
||||||
|
// If the device is in use, open will return an invalid fd.
|
||||||
|
// When this happens, it is expected that Close will fail and throw an error.
|
||||||
|
defer unix.Close(fd)
|
||||||
|
if errno == nil {
|
||||||
|
// device not in use
|
||||||
|
return false, nil
|
||||||
|
} else if errno == unix.EBUSY {
|
||||||
|
// device is in use
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
// error during call to Open
|
||||||
|
return false, errno
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDeviceNameFromMount given a mount point, find the device name from its global mount point
|
||||||
|
func (hu *HostUtil) GetDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error) {
|
||||||
|
return getDeviceNameFromMount(mounter, mountPath, pluginMountDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDeviceNameFromMountLinux find the device name from /proc/mounts in which
|
||||||
|
// the mount path reference should match the given plugin mount directory. In case no mount path reference
|
||||||
|
// matches, returns the volume name taken from its given mountPath
|
||||||
|
func getDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error) {
|
||||||
|
refs, err := mounter.GetMountRefs(mountPath)
|
||||||
|
if err != nil {
|
||||||
|
klog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if len(refs) == 0 {
|
||||||
|
klog.V(4).Infof("Directory %s is not mounted", mountPath)
|
||||||
|
return "", fmt.Errorf("directory %s is not mounted", mountPath)
|
||||||
|
}
|
||||||
|
for _, ref := range refs {
|
||||||
|
if strings.HasPrefix(ref, pluginMountDir) {
|
||||||
|
volumeID, err := filepath.Rel(pluginMountDir, ref)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return volumeID, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.Base(mountPath), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRShared checks that given path is on a mount with 'rshared' mount
|
||||||
|
// propagation. If not, it bind-mounts the path as rshared.
|
||||||
|
func (hu *HostUtil) MakeRShared(path string) error {
|
||||||
|
return DoMakeRShared(path, procMountInfoPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileType checks for file/directory/socket/block/character devices.
|
||||||
|
func (hu *HostUtil) GetFileType(pathname string) (FileType, error) {
|
||||||
|
return getFileType(pathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathExists tests if the given path already exists
|
||||||
|
// Error is returned on any other error than "file not found".
|
||||||
|
func (hu *HostUtil) PathExists(pathname string) (bool, error) {
|
||||||
|
return utilpath.Exists(utilpath.CheckFollowSymlink, pathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvalHostSymlinks returns the path name after evaluating symlinks.
|
||||||
|
// TODO once the nsenter implementation is removed, this method can be removed
|
||||||
|
// from the interface and filepath.EvalSymlinks used directly
|
||||||
|
func (hu *HostUtil) EvalHostSymlinks(pathname string) (string, error) {
|
||||||
|
return filepath.EvalSymlinks(pathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMountInfo returns the mount info on the given path.
|
||||||
|
func (hu *HostUtil) FindMountInfo(path string) (mount.MountInfo, error) {
|
||||||
|
return findMountInfo(path, procMountInfoPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isShared returns true, if given path is on a mount point that has shared
|
||||||
|
// mount propagation.
|
||||||
|
func isShared(mount string, mountInfoPath string) (bool, error) {
|
||||||
|
info, err := findMountInfo(mount, mountInfoPath)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse optional parameters
|
||||||
|
for _, opt := range info.OptionalFields {
|
||||||
|
if strings.HasPrefix(opt, "shared:") {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findMountInfo(path, mountInfoPath string) (mount.MountInfo, error) {
|
||||||
|
infos, err := mount.ParseMountInfo(mountInfoPath)
|
||||||
|
if err != nil {
|
||||||
|
return mount.MountInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// process /proc/xxx/mountinfo in backward order and find the first mount
|
||||||
|
// point that is prefix of 'path' - that's the mount where path resides
|
||||||
|
var info *mount.MountInfo
|
||||||
|
for i := len(infos) - 1; i >= 0; i-- {
|
||||||
|
if mount.PathWithinBase(path, infos[i].MountPoint) {
|
||||||
|
info = &infos[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if info == nil {
|
||||||
|
return mount.MountInfo{}, fmt.Errorf("cannot find mount point for %q", path)
|
||||||
|
}
|
||||||
|
return *info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DoMakeRShared is common implementation of MakeRShared on Linux. It checks if
|
||||||
|
// path is shared and bind-mounts it as rshared if needed. mountCmd and
|
||||||
|
// mountArgs are expected to contain mount-like command, DoMakeRShared will add
|
||||||
|
// '--bind <path> <path>' and '--make-rshared <path>' to mountArgs.
|
||||||
|
func DoMakeRShared(path string, mountInfoFilename string) error {
|
||||||
|
shared, err := isShared(path, mountInfoFilename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if shared {
|
||||||
|
klog.V(4).Infof("Directory %s is already on a shared mount", path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.V(2).Infof("Bind-mounting %q with shared mount propagation", path)
|
||||||
|
// mount --bind /var/lib/kubelet /var/lib/kubelet
|
||||||
|
if err := syscall.Mount(path, path, "" /*fstype*/, syscall.MS_BIND, "" /*data*/); err != nil {
|
||||||
|
return fmt.Errorf("failed to bind-mount %s: %v", path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mount --make-rshared /var/lib/kubelet
|
||||||
|
if err := syscall.Mount(path, path, "" /*fstype*/, syscall.MS_SHARED|syscall.MS_REC, "" /*data*/); err != nil {
|
||||||
|
return fmt.Errorf("failed to make %s rshared: %v", path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSELinux is common implementation of GetSELinuxSupport on Linux.
|
||||||
|
func GetSELinux(path string, mountInfoFilename string) (bool, error) {
|
||||||
|
info, err := findMountInfo(path, mountInfoFilename)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// "seclabel" can be both in mount options and super options.
|
||||||
|
for _, opt := range info.SuperOptions {
|
||||||
|
if opt == "seclabel" {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, opt := range info.MountOptions {
|
||||||
|
if opt == "seclabel" {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSELinuxSupport returns true if given path is on a mount that supports
|
||||||
|
// SELinux.
|
||||||
|
func (hu *HostUtil) GetSELinuxSupport(pathname string) (bool, error) {
|
||||||
|
return GetSELinux(pathname, procMountInfoPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOwner returns the integer ID for the user and group of the given path
|
||||||
|
func (hu *HostUtil) GetOwner(pathname string) (int64, int64, error) {
|
||||||
|
realpath, err := filepath.EvalSymlinks(pathname)
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, err
|
||||||
|
}
|
||||||
|
return GetOwnerLinux(realpath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMode returns permissions of the path.
|
||||||
|
func (hu *HostUtil) GetMode(pathname string) (os.FileMode, error) {
|
||||||
|
return GetModeLinux(pathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOwnerLinux is shared between Linux and NsEnterMounter
|
||||||
|
// pathname must already be evaluated for symlinks
|
||||||
|
func GetOwnerLinux(pathname string) (int64, int64, error) {
|
||||||
|
info, err := os.Stat(pathname)
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, err
|
||||||
|
}
|
||||||
|
stat := info.Sys().(*syscall.Stat_t)
|
||||||
|
return int64(stat.Uid), int64(stat.Gid), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetModeLinux is shared between Linux and NsEnterMounter
|
||||||
|
func GetModeLinux(pathname string) (os.FileMode, error) {
|
||||||
|
info, err := os.Stat(pathname)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return info.Mode(), nil
|
||||||
|
}
|
||||||
102
vendor/k8s.io/kubernetes/pkg/volume/util/hostutil/hostutil_unsupported.go
generated
vendored
Normal file
102
vendor/k8s.io/kubernetes/pkg/volume/util/hostutil/hostutil_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// +build !linux,!windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package hostutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"k8s.io/mount-utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HostUtil is an HostUtils implementation that allows compilation on
|
||||||
|
// unsupported platforms
|
||||||
|
type HostUtil struct{}
|
||||||
|
|
||||||
|
// NewHostUtil returns a struct that implements the HostUtils interface on
|
||||||
|
// unsupported platforms
|
||||||
|
func NewHostUtil() *HostUtil {
|
||||||
|
return &HostUtil{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var errUnsupported = errors.New("volume/util/hostutil on this platform is not supported")
|
||||||
|
|
||||||
|
// DeviceOpened always returns an error on unsupported platforms
|
||||||
|
func (hu *HostUtil) DeviceOpened(pathname string) (bool, error) {
|
||||||
|
return false, errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathIsDevice always returns an error on unsupported platforms
|
||||||
|
func (hu *HostUtil) PathIsDevice(pathname string) (bool, error) {
|
||||||
|
return true, errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDeviceNameFromMount always returns an error on unsupported platforms
|
||||||
|
func (hu *HostUtil) GetDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error) {
|
||||||
|
return getDeviceNameFromMount(mounter, mountPath, pluginMountDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRShared always returns an error on unsupported platforms
|
||||||
|
func (hu *HostUtil) MakeRShared(path string) error {
|
||||||
|
return errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileType always returns an error on unsupported platforms
|
||||||
|
func (hu *HostUtil) GetFileType(pathname string) (FileType, error) {
|
||||||
|
return FileType("fake"), errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeFile always returns an error on unsupported platforms
|
||||||
|
func (hu *HostUtil) MakeFile(pathname string) error {
|
||||||
|
return errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeDir always returns an error on unsupported platforms
|
||||||
|
func (hu *HostUtil) MakeDir(pathname string) error {
|
||||||
|
return errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathExists always returns an error on unsupported platforms
|
||||||
|
func (hu *HostUtil) PathExists(pathname string) (bool, error) {
|
||||||
|
return true, errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvalHostSymlinks always returns an error on unsupported platforms
|
||||||
|
func (hu *HostUtil) EvalHostSymlinks(pathname string) (string, error) {
|
||||||
|
return "", errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOwner always returns an error on unsupported platforms
|
||||||
|
func (hu *HostUtil) GetOwner(pathname string) (int64, int64, error) {
|
||||||
|
return -1, -1, errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSELinuxSupport always returns an error on unsupported platforms
|
||||||
|
func (hu *HostUtil) GetSELinuxSupport(pathname string) (bool, error) {
|
||||||
|
return false, errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetMode always returns an error on unsupported platforms
|
||||||
|
func (hu *HostUtil) GetMode(pathname string) (os.FileMode, error) {
|
||||||
|
return 0, errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error) {
|
||||||
|
return "", errUnsupported
|
||||||
|
}
|
||||||
124
vendor/k8s.io/kubernetes/pkg/volume/util/hostutil/hostutil_windows.go
generated
vendored
Normal file
124
vendor/k8s.io/kubernetes/pkg/volume/util/hostutil/hostutil_windows.go
generated
vendored
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package hostutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
"k8s.io/mount-utils"
|
||||||
|
utilpath "k8s.io/utils/path"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HostUtil implements HostUtils for Windows platforms.
|
||||||
|
type HostUtil struct{}
|
||||||
|
|
||||||
|
// NewHostUtil returns a struct that implements HostUtils on Windows platforms
|
||||||
|
func NewHostUtil() *HostUtil {
|
||||||
|
return &HostUtil{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDeviceNameFromMount given a mnt point, find the device
|
||||||
|
func (hu *HostUtil) GetDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error) {
|
||||||
|
return getDeviceNameFromMount(mounter, mountPath, pluginMountDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDeviceNameFromMount find the device(drive) name in which
|
||||||
|
// the mount path reference should match the given plugin mount directory. In case no mount path reference
|
||||||
|
// matches, returns the volume name taken from its given mountPath
|
||||||
|
func getDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error) {
|
||||||
|
refs, err := mounter.GetMountRefs(mountPath)
|
||||||
|
if err != nil {
|
||||||
|
klog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if len(refs) == 0 {
|
||||||
|
return "", fmt.Errorf("directory %s is not mounted", mountPath)
|
||||||
|
}
|
||||||
|
basemountPath := mount.NormalizeWindowsPath(pluginMountDir)
|
||||||
|
for _, ref := range refs {
|
||||||
|
if strings.Contains(ref, basemountPath) {
|
||||||
|
volumeID, err := filepath.Rel(mount.NormalizeWindowsPath(basemountPath), ref)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return volumeID, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.Base(mountPath), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceOpened determines if the device is in use elsewhere
|
||||||
|
func (hu *HostUtil) DeviceOpened(pathname string) (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathIsDevice determines if a path is a device.
|
||||||
|
func (hu *HostUtil) PathIsDevice(pathname string) (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRShared checks that given path is on a mount with 'rshared' mount
|
||||||
|
// propagation. Empty implementation here.
|
||||||
|
func (hu *HostUtil) MakeRShared(path string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileType checks for sockets/block/character devices
|
||||||
|
func (hu *(HostUtil)) GetFileType(pathname string) (FileType, error) {
|
||||||
|
return getFileType(pathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathExists checks whether the path exists
|
||||||
|
func (hu *HostUtil) PathExists(pathname string) (bool, error) {
|
||||||
|
return utilpath.Exists(utilpath.CheckFollowSymlink, pathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvalHostSymlinks returns the path name after evaluating symlinks
|
||||||
|
func (hu *HostUtil) EvalHostSymlinks(pathname string) (string, error) {
|
||||||
|
return filepath.EvalSymlinks(pathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOwner returns the integer ID for the user and group of the given path
|
||||||
|
// Note that on windows, it always returns 0. We actually don't set Group on
|
||||||
|
// windows platform, see SetVolumeOwnership implementation.
|
||||||
|
func (hu *HostUtil) GetOwner(pathname string) (int64, int64, error) {
|
||||||
|
return -1, -1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSELinuxSupport returns a boolean indicating support for SELinux.
|
||||||
|
// Windows does not support SELinux.
|
||||||
|
func (hu *HostUtil) GetSELinuxSupport(pathname string) (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMode returns permissions of the path.
|
||||||
|
func (hu *HostUtil) GetMode(pathname string) (os.FileMode, error) {
|
||||||
|
info, err := os.Stat(pathname)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return info.Mode(), nil
|
||||||
|
}
|
||||||
44
vendor/k8s.io/kubernetes/pkg/volume/util/recyclerclient/BUILD
generated
vendored
Normal file
44
vendor/k8s.io/kubernetes/pkg/volume/util/recyclerclient/BUILD
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["recycler_client.go"],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/volume/util/recyclerclient",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["recycler_client_test.go"],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/apis/core:go_default_library",
|
||||||
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/watch: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"],
|
||||||
|
)
|
||||||
266
vendor/k8s.io/kubernetes/pkg/volume/util/recyclerclient/recycler_client.go
generated
vendored
Normal file
266
vendor/k8s.io/kubernetes/pkg/volume/util/recyclerclient/recycler_client.go
generated
vendored
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package recyclerclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RecycleEventRecorder is a func that defines how to record RecycleEvent.
|
||||||
|
type RecycleEventRecorder func(eventtype, message string)
|
||||||
|
|
||||||
|
// RecycleVolumeByWatchingPodUntilCompletion is intended for use with volume
|
||||||
|
// Recyclers. This function will save the given Pod to the API and watch it
|
||||||
|
// until it completes, fails, or the pod's ActiveDeadlineSeconds is exceeded,
|
||||||
|
// whichever comes first. An attempt to delete a recycler pod is always
|
||||||
|
// attempted before returning.
|
||||||
|
//
|
||||||
|
// In case there is a pod with the same namespace+name already running, this
|
||||||
|
// function deletes it as it is not able to judge if it is an old recycler
|
||||||
|
// or user has forged a fake recycler to block Kubernetes from recycling.//
|
||||||
|
//
|
||||||
|
// pod - the pod designed by a volume plugin to recycle the volume. pod.Name
|
||||||
|
// will be overwritten with unique name based on PV.Name.
|
||||||
|
// client - kube client for API operations.
|
||||||
|
func RecycleVolumeByWatchingPodUntilCompletion(pvName string, pod *v1.Pod, kubeClient clientset.Interface, recorder RecycleEventRecorder) error {
|
||||||
|
return internalRecycleVolumeByWatchingPodUntilCompletion(pvName, pod, newRecyclerClient(kubeClient, recorder))
|
||||||
|
}
|
||||||
|
|
||||||
|
// same as above func comments, except 'recyclerClient' is a narrower pod API
|
||||||
|
// interface to ease testing
|
||||||
|
func internalRecycleVolumeByWatchingPodUntilCompletion(pvName string, pod *v1.Pod, recyclerClient recyclerClient) error {
|
||||||
|
klog.V(5).Infof("creating recycler pod for volume %s\n", pod.Name)
|
||||||
|
|
||||||
|
// Generate unique name for the recycler pod - we need to get "already
|
||||||
|
// exists" error when a previous controller has already started recycling
|
||||||
|
// the volume. Here we assume that pv.Name is already unique.
|
||||||
|
pod.Name = "recycler-for-" + pvName
|
||||||
|
pod.GenerateName = ""
|
||||||
|
|
||||||
|
stopChannel := make(chan struct{})
|
||||||
|
defer close(stopChannel)
|
||||||
|
podCh, err := recyclerClient.WatchPod(pod.Name, pod.Namespace, stopChannel)
|
||||||
|
if err != nil {
|
||||||
|
klog.V(4).Infof("cannot start watcher for pod %s/%s: %v", pod.Namespace, pod.Name, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the pod
|
||||||
|
_, err = recyclerClient.CreatePod(pod)
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsAlreadyExists(err) {
|
||||||
|
deleteErr := recyclerClient.DeletePod(pod.Name, pod.Namespace)
|
||||||
|
if deleteErr != nil {
|
||||||
|
return fmt.Errorf("failed to delete old recycler pod %s/%s: %s", pod.Namespace, pod.Name, deleteErr)
|
||||||
|
}
|
||||||
|
// Recycler will try again and the old pod will be hopefully deleted
|
||||||
|
// at that time.
|
||||||
|
return fmt.Errorf("old recycler pod found, will retry later")
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unexpected error creating recycler pod: %+v", err)
|
||||||
|
}
|
||||||
|
err = waitForPod(pod, recyclerClient, podCh)
|
||||||
|
|
||||||
|
// In all cases delete the recycler pod and log its result.
|
||||||
|
klog.V(2).Infof("deleting recycler pod %s/%s", pod.Namespace, pod.Name)
|
||||||
|
deleteErr := recyclerClient.DeletePod(pod.Name, pod.Namespace)
|
||||||
|
if deleteErr != nil {
|
||||||
|
klog.Errorf("failed to delete recycler pod %s/%s: %v", pod.Namespace, pod.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returning recycler error is preferred, the pod will be deleted again on
|
||||||
|
// the next retry.
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to recycle volume: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recycle succeeded but we failed to delete the recycler pod. Report it,
|
||||||
|
// the controller will re-try recycling the PV again shortly.
|
||||||
|
if deleteErr != nil {
|
||||||
|
return fmt.Errorf("failed to delete recycler pod: %s", deleteErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForPod watches the pod it until it finishes and send all events on the
|
||||||
|
// pod to the PV.
|
||||||
|
func waitForPod(pod *v1.Pod, recyclerClient recyclerClient, podCh <-chan watch.Event) error {
|
||||||
|
for {
|
||||||
|
event, ok := <-podCh
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("recycler pod %q watch channel had been closed", pod.Name)
|
||||||
|
}
|
||||||
|
switch event.Object.(type) {
|
||||||
|
case *v1.Pod:
|
||||||
|
// POD changed
|
||||||
|
pod := event.Object.(*v1.Pod)
|
||||||
|
klog.V(4).Infof("recycler pod update received: %s %s/%s %s", event.Type, pod.Namespace, pod.Name, pod.Status.Phase)
|
||||||
|
switch event.Type {
|
||||||
|
case watch.Added, watch.Modified:
|
||||||
|
if pod.Status.Phase == v1.PodSucceeded {
|
||||||
|
// Recycle succeeded.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if pod.Status.Phase == v1.PodFailed {
|
||||||
|
if pod.Status.Message != "" {
|
||||||
|
return fmt.Errorf(pod.Status.Message)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("pod failed, pod.Status.Message unknown")
|
||||||
|
}
|
||||||
|
|
||||||
|
case watch.Deleted:
|
||||||
|
return fmt.Errorf("recycler pod was deleted")
|
||||||
|
|
||||||
|
case watch.Error:
|
||||||
|
return fmt.Errorf("recycler pod watcher failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
case *v1.Event:
|
||||||
|
// Event received
|
||||||
|
podEvent := event.Object.(*v1.Event)
|
||||||
|
klog.V(4).Infof("recycler event received: %s %s/%s %s/%s %s", event.Type, podEvent.Namespace, podEvent.Name, podEvent.InvolvedObject.Namespace, podEvent.InvolvedObject.Name, podEvent.Message)
|
||||||
|
if event.Type == watch.Added {
|
||||||
|
recyclerClient.Event(podEvent.Type, podEvent.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// recyclerClient abstracts access to a Pod by providing a narrower interface.
|
||||||
|
// This makes it easier to mock a client for testing.
|
||||||
|
type recyclerClient interface {
|
||||||
|
CreatePod(pod *v1.Pod) (*v1.Pod, error)
|
||||||
|
GetPod(name, namespace string) (*v1.Pod, error)
|
||||||
|
DeletePod(name, namespace string) error
|
||||||
|
// WatchPod returns a ListWatch for watching a pod. The stopChannel is used
|
||||||
|
// to close the reflector backing the watch. The caller is responsible for
|
||||||
|
// derring a close on the channel to stop the reflector.
|
||||||
|
WatchPod(name, namespace string, stopChannel chan struct{}) (<-chan watch.Event, error)
|
||||||
|
// Event sends an event to the volume that is being recycled.
|
||||||
|
Event(eventtype, message string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRecyclerClient(client clientset.Interface, recorder RecycleEventRecorder) recyclerClient {
|
||||||
|
return &realRecyclerClient{
|
||||||
|
client,
|
||||||
|
recorder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type realRecyclerClient struct {
|
||||||
|
client clientset.Interface
|
||||||
|
recorder RecycleEventRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *realRecyclerClient) CreatePod(pod *v1.Pod) (*v1.Pod, error) {
|
||||||
|
return c.client.CoreV1().Pods(pod.Namespace).Create(context.TODO(), pod, metav1.CreateOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *realRecyclerClient) GetPod(name, namespace string) (*v1.Pod, error) {
|
||||||
|
return c.client.CoreV1().Pods(namespace).Get(context.TODO(), name, metav1.GetOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *realRecyclerClient) DeletePod(name, namespace string) error {
|
||||||
|
return c.client.CoreV1().Pods(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *realRecyclerClient) Event(eventtype, message string) {
|
||||||
|
c.recorder(eventtype, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WatchPod watches a pod and events related to it. It sends pod updates and events over the returned channel
|
||||||
|
// It will continue until stopChannel is closed
|
||||||
|
func (c *realRecyclerClient) WatchPod(name, namespace string, stopChannel chan struct{}) (<-chan watch.Event, error) {
|
||||||
|
podSelector, err := fields.ParseSelector("metadata.name=" + name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
options := metav1.ListOptions{
|
||||||
|
FieldSelector: podSelector.String(),
|
||||||
|
Watch: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
podWatch, err := c.client.CoreV1().Pods(namespace).Watch(context.TODO(), options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
eventSelector, _ := fields.ParseSelector("involvedObject.name=" + name)
|
||||||
|
eventWatch, err := c.client.CoreV1().Events(namespace).Watch(context.TODO(), metav1.ListOptions{
|
||||||
|
FieldSelector: eventSelector.String(),
|
||||||
|
Watch: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
podWatch.Stop()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
eventCh := make(chan watch.Event, 30)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(eventCh)
|
||||||
|
wg.Wait()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer eventWatch.Stop()
|
||||||
|
defer wg.Done()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stopChannel:
|
||||||
|
return
|
||||||
|
case eventEvent, ok := <-eventWatch.ResultChan():
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
eventCh <- eventEvent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer podWatch.Stop()
|
||||||
|
defer wg.Done()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stopChannel:
|
||||||
|
return
|
||||||
|
|
||||||
|
case podEvent, ok := <-podWatch.ResultChan():
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
eventCh <- podEvent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return eventCh, nil
|
||||||
|
}
|
||||||
118
vendor/k8s.io/kubernetes/pkg/volume/util/subpath/BUILD
generated
vendored
Normal file
118
vendor/k8s.io/kubernetes/pkg/volume/util/subpath/BUILD
generated
vendored
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"subpath.go",
|
||||||
|
"subpath_linux.go",
|
||||||
|
"subpath_unsupported.go",
|
||||||
|
"subpath_windows.go",
|
||||||
|
],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/volume/util/subpath",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = select({
|
||||||
|
"@io_bazel_rules_go//go/platform:aix": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/nsenter:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
|
"//pkg/volume/util/hostutil:go_default_library",
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:darwin": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/nsenter:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/nsenter:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/nsenter:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:illumos": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/nsenter:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:ios": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/nsenter:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:js": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/nsenter:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
|
"//pkg/volume/util/hostutil:go_default_library",
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:nacl": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/nsenter:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/nsenter:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/nsenter:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:plan9": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/nsenter:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:solaris": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/nsenter:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:windows": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/nsenter:go_default_library",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = [
|
||||||
|
"subpath_linux_test.go",
|
||||||
|
"subpath_windows_test.go",
|
||||||
|
],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = select({
|
||||||
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
|
"//staging/src/k8s.io/mount-utils:go_default_library",
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:windows": [
|
||||||
|
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
13
vendor/k8s.io/kubernetes/pkg/volume/util/subpath/OWNERS
generated
vendored
Normal file
13
vendor/k8s.io/kubernetes/pkg/volume/util/subpath/OWNERS
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# See the OWNERS docs at https://go.k8s.io/owners
|
||||||
|
|
||||||
|
reviewers:
|
||||||
|
- jingxu97
|
||||||
|
- saad-ali
|
||||||
|
- jsafrane
|
||||||
|
- msau42
|
||||||
|
- andyzhangx
|
||||||
|
approvers:
|
||||||
|
- jingxu97
|
||||||
|
- saad-ali
|
||||||
|
- jsafrane
|
||||||
|
|
||||||
92
vendor/k8s.io/kubernetes/pkg/volume/util/subpath/subpath.go
generated
vendored
Normal file
92
vendor/k8s.io/kubernetes/pkg/volume/util/subpath/subpath.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package subpath
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
// Interface defines the set of methods all subpathers must implement
|
||||||
|
type Interface interface {
|
||||||
|
// CleanSubPaths removes any bind-mounts created by PrepareSafeSubpath in given
|
||||||
|
// pod volume directory.
|
||||||
|
CleanSubPaths(poodDir string, volumeName string) error
|
||||||
|
|
||||||
|
// PrepareSafeSubpath does everything that's necessary to prepare a subPath
|
||||||
|
// that's 1) inside given volumePath and 2) immutable after this call.
|
||||||
|
//
|
||||||
|
// newHostPath - location of prepared subPath. It should be used instead of
|
||||||
|
// hostName when running the container.
|
||||||
|
// cleanupAction - action to run when the container is running or it failed to start.
|
||||||
|
//
|
||||||
|
// CleanupAction must be called immediately after the container with given
|
||||||
|
// subpath starts. On the other hand, Interface.CleanSubPaths must be called
|
||||||
|
// when the pod finishes.
|
||||||
|
PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error)
|
||||||
|
|
||||||
|
// SafeMakeDir creates subdir within given base. It makes sure that the
|
||||||
|
// created directory does not escape given base directory mis-using
|
||||||
|
// symlinks. Note that the function makes sure that it creates the directory
|
||||||
|
// somewhere under the base, nothing else. E.g. if the directory already
|
||||||
|
// exists, it may exist outside of the base due to symlinks.
|
||||||
|
// This method should be used if the directory to create is inside volume
|
||||||
|
// that's under user control. User must not be able to use symlinks to
|
||||||
|
// escape the volume to create directories somewhere else.
|
||||||
|
SafeMakeDir(subdir string, base string, perm os.FileMode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subpath defines the attributes of a subpath
|
||||||
|
type Subpath struct {
|
||||||
|
// index of the VolumeMount for this container
|
||||||
|
VolumeMountIndex int
|
||||||
|
|
||||||
|
// Full path to the subpath directory on the host
|
||||||
|
Path string
|
||||||
|
|
||||||
|
// name of the volume that is a valid directory name.
|
||||||
|
VolumeName string
|
||||||
|
|
||||||
|
// Full path to the volume path
|
||||||
|
VolumePath string
|
||||||
|
|
||||||
|
// Path to the pod's directory, including pod UID
|
||||||
|
PodDir string
|
||||||
|
|
||||||
|
// Name of the container
|
||||||
|
ContainerName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile time-check for all implementers of subpath interface
|
||||||
|
var _ Interface = &subpath{}
|
||||||
|
var _ Interface = &FakeSubpath{}
|
||||||
|
|
||||||
|
// FakeSubpath is a subpather implementation for testing
|
||||||
|
type FakeSubpath struct{}
|
||||||
|
|
||||||
|
// PrepareSafeSubpath is a fake implementation of PrepareSafeSubpath. Always returns
|
||||||
|
// newHostPath == subPath.Path
|
||||||
|
func (fs *FakeSubpath) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||||
|
return subPath.Path, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanSubPaths is a fake implementation of CleanSubPaths. It is a noop
|
||||||
|
func (fs *FakeSubpath) CleanSubPaths(podDir string, volumeName string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SafeMakeDir is a fake implementation of SafeMakeDir. It is a noop
|
||||||
|
func (fs *FakeSubpath) SafeMakeDir(pathname string, base string, perm os.FileMode) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
581
vendor/k8s.io/kubernetes/pkg/volume/util/subpath/subpath_linux.go
generated
vendored
Normal file
581
vendor/k8s.io/kubernetes/pkg/volume/util/subpath/subpath_linux.go
generated
vendored
Normal file
@ -0,0 +1,581 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package subpath
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/util/hostutil"
|
||||||
|
"k8s.io/mount-utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// place for subpath mounts
|
||||||
|
// TODO: pass in directory using kubelet_getters instead
|
||||||
|
containerSubPathDirectoryName = "volume-subpaths"
|
||||||
|
// syscall.Openat flags used to traverse directories not following symlinks
|
||||||
|
nofollowFlags = unix.O_RDONLY | unix.O_NOFOLLOW
|
||||||
|
// flags for getting file descriptor without following the symlink
|
||||||
|
openFDFlags = unix.O_NOFOLLOW | unix.O_PATH
|
||||||
|
)
|
||||||
|
|
||||||
|
type subpath struct {
|
||||||
|
mounter mount.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a subpath.Interface for the current system
|
||||||
|
func New(mounter mount.Interface) Interface {
|
||||||
|
return &subpath{
|
||||||
|
mounter: mounter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *subpath) CleanSubPaths(podDir string, volumeName string) error {
|
||||||
|
return doCleanSubPaths(sp.mounter, podDir, volumeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *subpath) SafeMakeDir(subdir string, base string, perm os.FileMode) error {
|
||||||
|
realBase, err := filepath.EvalSymlinks(base)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error resolving symlinks in %s: %s", base, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
realFullPath := filepath.Join(realBase, subdir)
|
||||||
|
|
||||||
|
return doSafeMakeDir(realFullPath, realBase, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *subpath) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||||
|
newHostPath, err = doBindSubPath(sp.mounter, subPath)
|
||||||
|
|
||||||
|
// There is no action when the container starts. Bind-mount will be cleaned
|
||||||
|
// when container stops by CleanSubPaths.
|
||||||
|
cleanupAction = nil
|
||||||
|
return newHostPath, cleanupAction, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// This implementation is shared between Linux and NsEnter
|
||||||
|
func safeOpenSubPath(mounter mount.Interface, subpath Subpath) (int, error) {
|
||||||
|
if !mount.PathWithinBase(subpath.Path, subpath.VolumePath) {
|
||||||
|
return -1, fmt.Errorf("subpath %q not within volume path %q", subpath.Path, subpath.VolumePath)
|
||||||
|
}
|
||||||
|
fd, err := doSafeOpen(subpath.Path, subpath.VolumePath)
|
||||||
|
if err != nil {
|
||||||
|
return -1, fmt.Errorf("error opening subpath %v: %v", subpath.Path, err)
|
||||||
|
}
|
||||||
|
return fd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareSubpathTarget creates target for bind-mount of subpath. It returns
|
||||||
|
// "true" when the target already exists and something is mounted there.
|
||||||
|
// Given Subpath must have all paths with already resolved symlinks and with
|
||||||
|
// paths relevant to kubelet (when it runs in a container).
|
||||||
|
// This function is called also by NsEnterMounter. It works because
|
||||||
|
// /var/lib/kubelet is mounted from the host into the container with Kubelet as
|
||||||
|
// /var/lib/kubelet too.
|
||||||
|
func prepareSubpathTarget(mounter mount.Interface, subpath Subpath) (bool, string, error) {
|
||||||
|
// Early check for already bind-mounted subpath.
|
||||||
|
bindPathTarget := getSubpathBindTarget(subpath)
|
||||||
|
notMount, err := mount.IsNotMountPoint(mounter, bindPathTarget)
|
||||||
|
if err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return false, "", fmt.Errorf("error checking path %s for mount: %s", bindPathTarget, err)
|
||||||
|
}
|
||||||
|
// Ignore ErrorNotExist: the file/directory will be created below if it does not exist yet.
|
||||||
|
notMount = true
|
||||||
|
}
|
||||||
|
if !notMount {
|
||||||
|
linuxHostUtil := hostutil.NewHostUtil()
|
||||||
|
mntInfo, err := linuxHostUtil.FindMountInfo(bindPathTarget)
|
||||||
|
if err != nil {
|
||||||
|
return false, "", fmt.Errorf("error calling findMountInfo for %s: %s", bindPathTarget, err)
|
||||||
|
}
|
||||||
|
if mntInfo.Root != subpath.Path {
|
||||||
|
// It's already mounted but not what we want, unmount it
|
||||||
|
if err = mounter.Unmount(bindPathTarget); err != nil {
|
||||||
|
return false, "", fmt.Errorf("error ummounting %s: %s", bindPathTarget, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// It's already mounted
|
||||||
|
klog.V(5).Infof("Skipping bind-mounting subpath %s: already mounted", bindPathTarget)
|
||||||
|
return true, bindPathTarget, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindPathTarget is in /var/lib/kubelet and thus reachable without any
|
||||||
|
// translation even to containerized kubelet.
|
||||||
|
bindParent := filepath.Dir(bindPathTarget)
|
||||||
|
err = os.MkdirAll(bindParent, 0750)
|
||||||
|
if err != nil && !os.IsExist(err) {
|
||||||
|
return false, "", fmt.Errorf("error creating directory %s: %s", bindParent, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := os.Lstat(subpath.Path)
|
||||||
|
if err != nil {
|
||||||
|
return false, "", fmt.Errorf("lstat %s failed: %s", subpath.Path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Mode()&os.ModeDir > 0 {
|
||||||
|
if err = os.Mkdir(bindPathTarget, 0750); err != nil && !os.IsExist(err) {
|
||||||
|
return false, "", fmt.Errorf("error creating directory %s: %s", bindPathTarget, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// "/bin/touch <bindPathTarget>".
|
||||||
|
// A file is enough for all possible targets (symlink, device, pipe,
|
||||||
|
// socket, ...), bind-mounting them into a file correctly changes type
|
||||||
|
// of the target file.
|
||||||
|
if err = ioutil.WriteFile(bindPathTarget, []byte{}, 0640); err != nil {
|
||||||
|
return false, "", fmt.Errorf("error creating file %s: %s", bindPathTarget, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, bindPathTarget, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSubpathBindTarget(subpath Subpath) string {
|
||||||
|
// containerName is DNS label, i.e. safe as a directory name.
|
||||||
|
return filepath.Join(subpath.PodDir, containerSubPathDirectoryName, subpath.VolumeName, subpath.ContainerName, strconv.Itoa(subpath.VolumeMountIndex))
|
||||||
|
}
|
||||||
|
|
||||||
|
func doBindSubPath(mounter mount.Interface, subpath Subpath) (hostPath string, err error) {
|
||||||
|
// Linux, kubelet runs on the host:
|
||||||
|
// - safely open the subpath
|
||||||
|
// - bind-mount /proc/<pid of kubelet>/fd/<fd> to subpath target
|
||||||
|
// User can't change /proc/<pid of kubelet>/fd/<fd> to point to a bad place.
|
||||||
|
|
||||||
|
// Evaluate all symlinks here once for all subsequent functions.
|
||||||
|
newVolumePath, err := filepath.EvalSymlinks(subpath.VolumePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("error resolving symlinks in %q: %v", subpath.VolumePath, err)
|
||||||
|
}
|
||||||
|
newPath, err := filepath.EvalSymlinks(subpath.Path)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("error resolving symlinks in %q: %v", subpath.Path, err)
|
||||||
|
}
|
||||||
|
klog.V(5).Infof("doBindSubPath %q (%q) for volumepath %q", subpath.Path, newPath, subpath.VolumePath)
|
||||||
|
subpath.VolumePath = newVolumePath
|
||||||
|
subpath.Path = newPath
|
||||||
|
|
||||||
|
fd, err := safeOpenSubPath(mounter, subpath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer syscall.Close(fd)
|
||||||
|
|
||||||
|
alreadyMounted, bindPathTarget, err := prepareSubpathTarget(mounter, subpath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if alreadyMounted {
|
||||||
|
return bindPathTarget, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
success := false
|
||||||
|
defer func() {
|
||||||
|
// Cleanup subpath on error
|
||||||
|
if !success {
|
||||||
|
klog.V(4).Infof("doBindSubPath() failed for %q, cleaning up subpath", bindPathTarget)
|
||||||
|
if cleanErr := cleanSubPath(mounter, subpath); cleanErr != nil {
|
||||||
|
klog.Errorf("Failed to clean subpath %q: %v", bindPathTarget, cleanErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
kubeletPid := os.Getpid()
|
||||||
|
mountSource := fmt.Sprintf("/proc/%d/fd/%v", kubeletPid, fd)
|
||||||
|
|
||||||
|
// Do the bind mount
|
||||||
|
options := []string{"bind"}
|
||||||
|
klog.V(5).Infof("bind mounting %q at %q", mountSource, bindPathTarget)
|
||||||
|
if err = mounter.MountSensitiveWithoutSystemd(mountSource, bindPathTarget, "" /*fstype*/, options, nil); err != nil {
|
||||||
|
return "", fmt.Errorf("error mounting %s: %s", subpath.Path, err)
|
||||||
|
}
|
||||||
|
success = true
|
||||||
|
|
||||||
|
klog.V(3).Infof("Bound SubPath %s into %s", subpath.Path, bindPathTarget)
|
||||||
|
return bindPathTarget, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This implementation is shared between Linux and NsEnter
|
||||||
|
func doCleanSubPaths(mounter mount.Interface, podDir string, volumeName string) error {
|
||||||
|
// scan /var/lib/kubelet/pods/<uid>/volume-subpaths/<volume>/*
|
||||||
|
subPathDir := filepath.Join(podDir, containerSubPathDirectoryName, volumeName)
|
||||||
|
klog.V(4).Infof("Cleaning up subpath mounts for %s", subPathDir)
|
||||||
|
|
||||||
|
containerDirs, err := ioutil.ReadDir(subPathDir)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("error reading %s: %s", subPathDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, containerDir := range containerDirs {
|
||||||
|
if !containerDir.IsDir() {
|
||||||
|
klog.V(4).Infof("Container file is not a directory: %s", containerDir.Name())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
klog.V(4).Infof("Cleaning up subpath mounts for container %s", containerDir.Name())
|
||||||
|
|
||||||
|
// scan /var/lib/kubelet/pods/<uid>/volume-subpaths/<volume>/<container name>/*
|
||||||
|
fullContainerDirPath := filepath.Join(subPathDir, containerDir.Name())
|
||||||
|
err = filepath.Walk(fullContainerDirPath, func(path string, info os.FileInfo, _ error) error {
|
||||||
|
if path == fullContainerDirPath {
|
||||||
|
// Skip top level directory
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass through errors and let doCleanSubPath handle them
|
||||||
|
if err = doCleanSubPath(mounter, fullContainerDirPath, filepath.Base(path)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to check that info is not nil. This may happen when the incoming err is not nil due to stale mounts or permission errors.
|
||||||
|
if info != nil && info.IsDir() {
|
||||||
|
// skip subdirs of the volume: it only matters the first level to unmount, otherwise it would try to unmount subdir of the volume
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error processing %s: %s", fullContainerDirPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whole container has been processed, remove its directory.
|
||||||
|
if err := os.Remove(fullContainerDirPath); err != nil {
|
||||||
|
return fmt.Errorf("error deleting %s: %s", fullContainerDirPath, err)
|
||||||
|
}
|
||||||
|
klog.V(5).Infof("Removed %s", fullContainerDirPath)
|
||||||
|
}
|
||||||
|
// Whole pod volume subpaths have been cleaned up, remove its subpath directory.
|
||||||
|
if err := os.Remove(subPathDir); err != nil {
|
||||||
|
return fmt.Errorf("error deleting %s: %s", subPathDir, err)
|
||||||
|
}
|
||||||
|
klog.V(5).Infof("Removed %s", subPathDir)
|
||||||
|
|
||||||
|
// Remove entire subpath directory if it's the last one
|
||||||
|
podSubPathDir := filepath.Join(podDir, containerSubPathDirectoryName)
|
||||||
|
if err := os.Remove(podSubPathDir); err != nil && !os.IsExist(err) {
|
||||||
|
return fmt.Errorf("error deleting %s: %s", podSubPathDir, err)
|
||||||
|
}
|
||||||
|
klog.V(5).Infof("Removed %s", podSubPathDir)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// doCleanSubPath tears down the single subpath bind mount
|
||||||
|
func doCleanSubPath(mounter mount.Interface, fullContainerDirPath, subPathIndex string) error {
|
||||||
|
// process /var/lib/kubelet/pods/<uid>/volume-subpaths/<volume>/<container name>/<subPathName>
|
||||||
|
klog.V(4).Infof("Cleaning up subpath mounts for subpath %v", subPathIndex)
|
||||||
|
fullSubPath := filepath.Join(fullContainerDirPath, subPathIndex)
|
||||||
|
|
||||||
|
if err := mount.CleanupMountPoint(fullSubPath, mounter, true); err != nil {
|
||||||
|
return fmt.Errorf("error cleaning subpath mount %s: %s", fullSubPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.V(4).Infof("Successfully cleaned subpath directory %s", fullSubPath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanSubPath will teardown the subpath bind mount and any remove any directories if empty
|
||||||
|
func cleanSubPath(mounter mount.Interface, subpath Subpath) error {
|
||||||
|
containerDir := filepath.Join(subpath.PodDir, containerSubPathDirectoryName, subpath.VolumeName, subpath.ContainerName)
|
||||||
|
|
||||||
|
// Clean subdir bindmount
|
||||||
|
if err := doCleanSubPath(mounter, containerDir, strconv.Itoa(subpath.VolumeMountIndex)); err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recusively remove directories if empty
|
||||||
|
if err := removeEmptyDirs(subpath.PodDir, containerDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeEmptyDirs works backwards from endDir to baseDir and removes each directory
|
||||||
|
// if it is empty. It stops once it encounters a directory that has content
|
||||||
|
func removeEmptyDirs(baseDir, endDir string) error {
|
||||||
|
if !mount.PathWithinBase(endDir, baseDir) {
|
||||||
|
return fmt.Errorf("endDir %q is not within baseDir %q", endDir, baseDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
for curDir := endDir; curDir != baseDir; curDir = filepath.Dir(curDir) {
|
||||||
|
s, err := os.Stat(curDir)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
klog.V(5).Infof("curDir %q doesn't exist, skipping", curDir)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return fmt.Errorf("error stat %q: %v", curDir, err)
|
||||||
|
}
|
||||||
|
if !s.IsDir() {
|
||||||
|
return fmt.Errorf("path %q not a directory", curDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Remove(curDir)
|
||||||
|
if os.IsExist(err) {
|
||||||
|
klog.V(5).Infof("Directory %q not empty, not removing", curDir)
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("error removing directory %q: %v", curDir, err)
|
||||||
|
}
|
||||||
|
klog.V(5).Infof("Removed directory %q", curDir)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This implementation is shared between Linux and NsEnterMounter. Both pathname
|
||||||
|
// and base must be either already resolved symlinks or thet will be resolved in
|
||||||
|
// kubelet's mount namespace (in case it runs containerized).
|
||||||
|
func doSafeMakeDir(pathname string, base string, perm os.FileMode) error {
|
||||||
|
klog.V(4).Infof("Creating directory %q within base %q", pathname, base)
|
||||||
|
|
||||||
|
if !mount.PathWithinBase(pathname, base) {
|
||||||
|
return fmt.Errorf("path %s is outside of allowed base %s", pathname, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick check if the directory already exists
|
||||||
|
s, err := os.Stat(pathname)
|
||||||
|
if err == nil {
|
||||||
|
// Path exists
|
||||||
|
if s.IsDir() {
|
||||||
|
// The directory already exists. It can be outside of the parent,
|
||||||
|
// but there is no race-proof check.
|
||||||
|
klog.V(4).Infof("Directory %s already exists", pathname)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &os.PathError{Op: "mkdir", Path: pathname, Err: syscall.ENOTDIR}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all existing directories
|
||||||
|
existingPath, toCreate, err := findExistingPrefix(base, pathname)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error opening directory %s: %s", pathname, err)
|
||||||
|
}
|
||||||
|
// Ensure the existing directory is inside allowed base
|
||||||
|
fullExistingPath, err := filepath.EvalSymlinks(existingPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error opening directory %s: %s", existingPath, err)
|
||||||
|
}
|
||||||
|
if !mount.PathWithinBase(fullExistingPath, base) {
|
||||||
|
return fmt.Errorf("path %s is outside of allowed base %s", fullExistingPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.V(4).Infof("%q already exists, %q to create", fullExistingPath, filepath.Join(toCreate...))
|
||||||
|
parentFD, err := doSafeOpen(fullExistingPath, base)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot open directory %s: %s", existingPath, err)
|
||||||
|
}
|
||||||
|
childFD := -1
|
||||||
|
defer func() {
|
||||||
|
if parentFD != -1 {
|
||||||
|
if err = syscall.Close(parentFD); err != nil {
|
||||||
|
klog.V(4).Infof("Closing FD %v failed for safemkdir(%v): %v", parentFD, pathname, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if childFD != -1 {
|
||||||
|
if err = syscall.Close(childFD); err != nil {
|
||||||
|
klog.V(4).Infof("Closing FD %v failed for safemkdir(%v): %v", childFD, pathname, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
currentPath := fullExistingPath
|
||||||
|
// create the directories one by one, making sure nobody can change
|
||||||
|
// created directory into symlink.
|
||||||
|
for _, dir := range toCreate {
|
||||||
|
currentPath = filepath.Join(currentPath, dir)
|
||||||
|
klog.V(4).Infof("Creating %s", dir)
|
||||||
|
err = syscall.Mkdirat(parentFD, currentPath, uint32(perm))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot create directory %s: %s", currentPath, err)
|
||||||
|
}
|
||||||
|
// Dive into the created directory
|
||||||
|
childFD, err = syscall.Openat(parentFD, dir, nofollowFlags|unix.O_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot open %s: %s", currentPath, err)
|
||||||
|
}
|
||||||
|
// We can be sure that childFD is safe to use. It could be changed
|
||||||
|
// by user after Mkdirat() and before Openat(), however:
|
||||||
|
// - it could not be changed to symlink - we use nofollowFlags
|
||||||
|
// - it could be changed to a file (or device, pipe, socket, ...)
|
||||||
|
// but either subsequent Mkdirat() fails or we mount this file
|
||||||
|
// to user's container. Security is no violated in both cases
|
||||||
|
// and user either gets error or the file that it can already access.
|
||||||
|
|
||||||
|
if err = syscall.Close(parentFD); err != nil {
|
||||||
|
klog.V(4).Infof("Closing FD %v failed for safemkdir(%v): %v", parentFD, pathname, err)
|
||||||
|
}
|
||||||
|
parentFD = childFD
|
||||||
|
childFD = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything was created. mkdirat(..., perm) above was affected by current
|
||||||
|
// umask and we must apply the right permissions to the last directory
|
||||||
|
// (that's the one that will be available to the container as subpath)
|
||||||
|
// so user can read/write it. This is the behavior of previous code.
|
||||||
|
// TODO: chmod all created directories, not just the last one.
|
||||||
|
// parentFD is the last created directory.
|
||||||
|
|
||||||
|
// Translate perm (os.FileMode) to uint32 that fchmod() expects
|
||||||
|
kernelPerm := uint32(perm & os.ModePerm)
|
||||||
|
if perm&os.ModeSetgid > 0 {
|
||||||
|
kernelPerm |= syscall.S_ISGID
|
||||||
|
}
|
||||||
|
if perm&os.ModeSetuid > 0 {
|
||||||
|
kernelPerm |= syscall.S_ISUID
|
||||||
|
}
|
||||||
|
if perm&os.ModeSticky > 0 {
|
||||||
|
kernelPerm |= syscall.S_ISVTX
|
||||||
|
}
|
||||||
|
if err = syscall.Fchmod(parentFD, kernelPerm); err != nil {
|
||||||
|
return fmt.Errorf("chmod %q failed: %s", currentPath, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// findExistingPrefix finds prefix of pathname that exists. In addition, it
|
||||||
|
// returns list of remaining directories that don't exist yet.
|
||||||
|
func findExistingPrefix(base, pathname string) (string, []string, error) {
|
||||||
|
rel, err := filepath.Rel(base, pathname)
|
||||||
|
if err != nil {
|
||||||
|
return base, nil, err
|
||||||
|
}
|
||||||
|
dirs := strings.Split(rel, string(filepath.Separator))
|
||||||
|
|
||||||
|
// Do OpenAt in a loop to find the first non-existing dir. Resolve symlinks.
|
||||||
|
// This should be faster than looping through all dirs and calling os.Stat()
|
||||||
|
// on each of them, as the symlinks are resolved only once with OpenAt().
|
||||||
|
currentPath := base
|
||||||
|
fd, err := syscall.Open(currentPath, syscall.O_RDONLY|syscall.O_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return pathname, nil, fmt.Errorf("error opening %s: %s", currentPath, err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err = syscall.Close(fd); err != nil {
|
||||||
|
klog.V(4).Infof("Closing FD %v failed for findExistingPrefix(%v): %v", fd, pathname, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for i, dir := range dirs {
|
||||||
|
// Using O_PATH here will prevent hangs in case user replaces directory with
|
||||||
|
// fifo
|
||||||
|
childFD, err := syscall.Openat(fd, dir, unix.O_PATH|unix.O_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return currentPath, dirs[i:], nil
|
||||||
|
}
|
||||||
|
return base, nil, err
|
||||||
|
}
|
||||||
|
if err = syscall.Close(fd); err != nil {
|
||||||
|
klog.V(4).Infof("Closing FD %v failed for findExistingPrefix(%v): %v", fd, pathname, err)
|
||||||
|
}
|
||||||
|
fd = childFD
|
||||||
|
currentPath = filepath.Join(currentPath, dir)
|
||||||
|
}
|
||||||
|
return pathname, []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This implementation is shared between Linux and NsEnterMounter
|
||||||
|
// Open path and return its fd.
|
||||||
|
// Symlinks are disallowed (pathname must already resolve symlinks),
|
||||||
|
// and the path must be within the base directory.
|
||||||
|
func doSafeOpen(pathname string, base string) (int, error) {
|
||||||
|
pathname = filepath.Clean(pathname)
|
||||||
|
base = filepath.Clean(base)
|
||||||
|
|
||||||
|
// Calculate segments to follow
|
||||||
|
subpath, err := filepath.Rel(base, pathname)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
segments := strings.Split(subpath, string(filepath.Separator))
|
||||||
|
|
||||||
|
// Assumption: base is the only directory that we have under control.
|
||||||
|
// Base dir is not allowed to be a symlink.
|
||||||
|
parentFD, err := syscall.Open(base, nofollowFlags|unix.O_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return -1, fmt.Errorf("cannot open directory %s: %s", base, err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if parentFD != -1 {
|
||||||
|
if err = syscall.Close(parentFD); err != nil {
|
||||||
|
klog.V(4).Infof("Closing FD %v failed for safeopen(%v): %v", parentFD, pathname, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
childFD := -1
|
||||||
|
defer func() {
|
||||||
|
if childFD != -1 {
|
||||||
|
if err = syscall.Close(childFD); err != nil {
|
||||||
|
klog.V(4).Infof("Closing FD %v failed for safeopen(%v): %v", childFD, pathname, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
currentPath := base
|
||||||
|
|
||||||
|
// Follow the segments one by one using openat() to make
|
||||||
|
// sure the user cannot change already existing directories into symlinks.
|
||||||
|
for _, seg := range segments {
|
||||||
|
currentPath = filepath.Join(currentPath, seg)
|
||||||
|
if !mount.PathWithinBase(currentPath, base) {
|
||||||
|
return -1, fmt.Errorf("path %s is outside of allowed base %s", currentPath, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.V(5).Infof("Opening path %s", currentPath)
|
||||||
|
childFD, err = syscall.Openat(parentFD, seg, openFDFlags|unix.O_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return -1, fmt.Errorf("cannot open %s: %s", currentPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var deviceStat unix.Stat_t
|
||||||
|
err := unix.Fstat(childFD, &deviceStat)
|
||||||
|
if err != nil {
|
||||||
|
return -1, fmt.Errorf("error running fstat on %s with %v", currentPath, err)
|
||||||
|
}
|
||||||
|
fileFmt := deviceStat.Mode & syscall.S_IFMT
|
||||||
|
if fileFmt == syscall.S_IFLNK {
|
||||||
|
return -1, fmt.Errorf("unexpected symlink found %s", currentPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close parentFD
|
||||||
|
if err = syscall.Close(parentFD); err != nil {
|
||||||
|
return -1, fmt.Errorf("closing fd for %q failed: %v", filepath.Dir(currentPath), err)
|
||||||
|
}
|
||||||
|
// Set child to new parent
|
||||||
|
parentFD = childFD
|
||||||
|
childFD = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// We made it to the end, return this fd, don't close it
|
||||||
|
finalFD := parentFD
|
||||||
|
parentFD = -1
|
||||||
|
|
||||||
|
return finalFD, nil
|
||||||
|
}
|
||||||
54
vendor/k8s.io/kubernetes/pkg/volume/util/subpath/subpath_unsupported.go
generated
vendored
Normal file
54
vendor/k8s.io/kubernetes/pkg/volume/util/subpath/subpath_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// +build !linux,!windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package subpath
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"k8s.io/mount-utils"
|
||||||
|
"k8s.io/utils/nsenter"
|
||||||
|
)
|
||||||
|
|
||||||
|
type subpath struct{}
|
||||||
|
|
||||||
|
var errUnsupported = errors.New("util/subpath on this platform is not supported")
|
||||||
|
|
||||||
|
// New returns a subpath.Interface for the current system.
|
||||||
|
func New(mount.Interface) Interface {
|
||||||
|
return &subpath{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNSEnter is to satisfy the compiler for having NewSubpathNSEnter exist for all
|
||||||
|
// OS choices. however, NSEnter is only valid on Linux
|
||||||
|
func NewNSEnter(mounter mount.Interface, ne *nsenter.Nsenter, rootDir string) Interface {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *subpath) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||||
|
return subPath.Path, nil, errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *subpath) CleanSubPaths(podDir string, volumeName string) error {
|
||||||
|
return errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *subpath) SafeMakeDir(pathname string, base string, perm os.FileMode) error {
|
||||||
|
return errUnsupported
|
||||||
|
}
|
||||||
377
vendor/k8s.io/kubernetes/pkg/volume/util/subpath/subpath_windows.go
generated
vendored
Normal file
377
vendor/k8s.io/kubernetes/pkg/volume/util/subpath/subpath_windows.go
generated
vendored
Normal file
@ -0,0 +1,377 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package subpath
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
"k8s.io/mount-utils"
|
||||||
|
"k8s.io/utils/nsenter"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxPathLength is the maximum length of Windows path. Normally, it is 260, but if long path is enable,
|
||||||
|
// the max number is 32,767
|
||||||
|
const MaxPathLength = 32767
|
||||||
|
|
||||||
|
type subpath struct{}
|
||||||
|
|
||||||
|
// New returns a subpath.Interface for the current system
|
||||||
|
func New(mount.Interface) Interface {
|
||||||
|
return &subpath{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNSEnter is to satisfy the compiler for having NewSubpathNSEnter exist for all
|
||||||
|
// OS choices. however, NSEnter is only valid on Linux
|
||||||
|
func NewNSEnter(mounter mount.Interface, ne *nsenter.Nsenter, rootDir string) Interface {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isDriveLetterPath returns true if the given path is empty or it ends with ":" or ":\" or ":\\"
|
||||||
|
func isDriveLetterorEmptyPath(path string) bool {
|
||||||
|
if path == "" || strings.HasSuffix(path, ":\\\\") || strings.HasSuffix(path, ":") || strings.HasSuffix(path, ":\\") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isVolumePrefix returns true if the given path name starts with "Volume" or volume prefix including
|
||||||
|
// "\\.\", "\\?\" for device path or "UNC" or "\\" for UNC path. Otherwise, it returns false.
|
||||||
|
func isDeviceOrUncPath(path string) bool {
|
||||||
|
if strings.HasPrefix(path, "Volume") || strings.HasPrefix(path, "\\\\?\\") || strings.HasPrefix(path, "\\\\.\\") || strings.HasPrefix(path, "UNC") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// getUpperPath removes the last level of directory.
|
||||||
|
func getUpperPath(path string) string {
|
||||||
|
sep := fmt.Sprintf("%c", filepath.Separator)
|
||||||
|
upperpath := strings.TrimSuffix(path, sep)
|
||||||
|
return filepath.Dir(upperpath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether a directory/file is a link type or not
|
||||||
|
// LinkType could be SymbolicLink, Junction, or HardLink
|
||||||
|
func isLinkPath(path string) (bool, error) {
|
||||||
|
cmd := fmt.Sprintf("(Get-Item -Path %s).LinkType", path)
|
||||||
|
output, err := exec.Command("powershell", "/c", cmd).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(string(output)) != "" {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalSymlink returns the path name after the evaluation of any symbolic links.
|
||||||
|
// If the path after evaluation is a device path or network connection, the original path is returned
|
||||||
|
func evalSymlink(path string) (string, error) {
|
||||||
|
path = mount.NormalizeWindowsPath(path)
|
||||||
|
if isDeviceOrUncPath(path) || isDriveLetterorEmptyPath(path) {
|
||||||
|
klog.V(4).Infof("Path '%s' is not a symlink, return its original form.", path)
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
upperpath := path
|
||||||
|
base := ""
|
||||||
|
for i := 0; i < MaxPathLength; i++ {
|
||||||
|
isLink, err := isLinkPath(upperpath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if isLink {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// continue to check next layer
|
||||||
|
base = filepath.Join(filepath.Base(upperpath), base)
|
||||||
|
upperpath = getUpperPath(upperpath)
|
||||||
|
if isDriveLetterorEmptyPath(upperpath) {
|
||||||
|
klog.V(4).Infof("Path '%s' is not a symlink, return its original form.", path)
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This command will give the target path of a given symlink
|
||||||
|
cmd := fmt.Sprintf("(Get-Item -Path %s).Target", upperpath)
|
||||||
|
output, err := exec.Command("powershell", "/c", cmd).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
klog.V(4).Infof("evaluate path %s: symlink from %s to %s", path, upperpath, string(output))
|
||||||
|
linkedPath := strings.TrimSpace(string(output))
|
||||||
|
if linkedPath == "" || isDeviceOrUncPath(linkedPath) {
|
||||||
|
klog.V(4).Infof("Path '%s' has a target %s. Return its original form.", path, linkedPath)
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
// If the target is not an absoluate path, join iit with the current upperpath
|
||||||
|
if !filepath.IsAbs(linkedPath) {
|
||||||
|
linkedPath = filepath.Join(getUpperPath(upperpath), linkedPath)
|
||||||
|
}
|
||||||
|
nextLink, err := evalSymlink(linkedPath)
|
||||||
|
if err != nil {
|
||||||
|
return path, err
|
||||||
|
}
|
||||||
|
return filepath.Join(nextLink, base), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether hostPath is within volume path
|
||||||
|
// this func will lock all intermediate subpath directories, need to close handle outside of this func after container started
|
||||||
|
func lockAndCheckSubPath(volumePath, hostPath string) ([]uintptr, error) {
|
||||||
|
if len(volumePath) == 0 || len(hostPath) == 0 {
|
||||||
|
return []uintptr{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
finalSubPath, err := evalSymlink(hostPath)
|
||||||
|
if err != nil {
|
||||||
|
return []uintptr{}, fmt.Errorf("cannot evaluate link %s: %s", hostPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
finalVolumePath, err := evalSymlink(volumePath)
|
||||||
|
if err != nil {
|
||||||
|
return []uintptr{}, fmt.Errorf("cannot read link %s: %s", volumePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lockAndCheckSubPathWithoutSymlink(finalVolumePath, finalSubPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// lock all intermediate subPath directories and check they are all within volumePath
|
||||||
|
// volumePath & subPath should not contain any symlink, otherwise it will return error
|
||||||
|
func lockAndCheckSubPathWithoutSymlink(volumePath, subPath string) ([]uintptr, error) {
|
||||||
|
if len(volumePath) == 0 || len(subPath) == 0 {
|
||||||
|
return []uintptr{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// get relative path to volumePath
|
||||||
|
relSubPath, err := filepath.Rel(volumePath, subPath)
|
||||||
|
if err != nil {
|
||||||
|
return []uintptr{}, fmt.Errorf("Rel(%s, %s) error: %v", volumePath, subPath, err)
|
||||||
|
}
|
||||||
|
if mount.StartsWithBackstep(relSubPath) {
|
||||||
|
return []uintptr{}, fmt.Errorf("SubPath %q not within volume path %q", subPath, volumePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if relSubPath == "." {
|
||||||
|
// volumePath and subPath are equal
|
||||||
|
return []uintptr{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fileHandles := []uintptr{}
|
||||||
|
var errorResult error
|
||||||
|
|
||||||
|
currentFullPath := volumePath
|
||||||
|
dirs := strings.Split(relSubPath, string(os.PathSeparator))
|
||||||
|
for _, dir := range dirs {
|
||||||
|
// lock intermediate subPath directory first
|
||||||
|
currentFullPath = filepath.Join(currentFullPath, dir)
|
||||||
|
handle, err := lockPath(currentFullPath)
|
||||||
|
if err != nil {
|
||||||
|
errorResult = fmt.Errorf("cannot lock path %s: %s", currentFullPath, err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fileHandles = append(fileHandles, handle)
|
||||||
|
|
||||||
|
// make sure intermediate subPath directory does not contain symlink any more
|
||||||
|
stat, err := os.Lstat(currentFullPath)
|
||||||
|
if err != nil {
|
||||||
|
errorResult = fmt.Errorf("Lstat(%q) error: %v", currentFullPath, err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if stat.Mode()&os.ModeSymlink != 0 {
|
||||||
|
errorResult = fmt.Errorf("subpath %q is an unexpected symlink after EvalSymlinks", currentFullPath)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mount.PathWithinBase(currentFullPath, volumePath) {
|
||||||
|
errorResult = fmt.Errorf("SubPath %q not within volume path %q", currentFullPath, volumePath)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileHandles, errorResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// unlockPath unlock directories
|
||||||
|
func unlockPath(fileHandles []uintptr) {
|
||||||
|
if fileHandles != nil {
|
||||||
|
for _, handle := range fileHandles {
|
||||||
|
syscall.CloseHandle(syscall.Handle(handle))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lockPath locks a directory or symlink, return handle, exec "syscall.CloseHandle(handle)" to unlock the path
|
||||||
|
func lockPath(path string) (uintptr, error) {
|
||||||
|
if len(path) == 0 {
|
||||||
|
return uintptr(syscall.InvalidHandle), syscall.ERROR_FILE_NOT_FOUND
|
||||||
|
}
|
||||||
|
pathp, err := syscall.UTF16PtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return uintptr(syscall.InvalidHandle), err
|
||||||
|
}
|
||||||
|
access := uint32(syscall.GENERIC_READ)
|
||||||
|
sharemode := uint32(syscall.FILE_SHARE_READ)
|
||||||
|
createmode := uint32(syscall.OPEN_EXISTING)
|
||||||
|
flags := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS | syscall.FILE_FLAG_OPEN_REPARSE_POINT)
|
||||||
|
fd, err := syscall.CreateFile(pathp, access, sharemode, nil, createmode, flags, 0)
|
||||||
|
return uintptr(fd), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock all directories in subPath and check they're not symlinks.
|
||||||
|
func (sp *subpath) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||||
|
handles, err := lockAndCheckSubPath(subPath.VolumePath, subPath.Path)
|
||||||
|
|
||||||
|
// Unlock the directories when the container starts
|
||||||
|
cleanupAction = func() {
|
||||||
|
unlockPath(handles)
|
||||||
|
}
|
||||||
|
return subPath.Path, cleanupAction, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// No bind-mounts for subpaths are necessary on Windows
|
||||||
|
func (sp *subpath) CleanSubPaths(podDir string, volumeName string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SafeMakeDir makes sure that the created directory does not escape given base directory mis-using symlinks.
|
||||||
|
func (sp *subpath) SafeMakeDir(subdir string, base string, perm os.FileMode) error {
|
||||||
|
realBase, err := evalSymlink(base)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error resolving symlinks in %s: %s", base, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
realFullPath := filepath.Join(realBase, subdir)
|
||||||
|
return doSafeMakeDir(realFullPath, realBase, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doSafeMakeDir(pathname string, base string, perm os.FileMode) error {
|
||||||
|
klog.V(4).Infof("Creating directory %q within base %q", pathname, base)
|
||||||
|
|
||||||
|
if !mount.PathWithinBase(pathname, base) {
|
||||||
|
return fmt.Errorf("path %s is outside of allowed base %s", pathname, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick check if the directory already exists
|
||||||
|
s, err := os.Stat(pathname)
|
||||||
|
if err == nil {
|
||||||
|
// Path exists
|
||||||
|
if s.IsDir() {
|
||||||
|
// The directory already exists. It can be outside of the parent,
|
||||||
|
// but there is no race-proof check.
|
||||||
|
klog.V(4).Infof("Directory %s already exists", pathname)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &os.PathError{Op: "mkdir", Path: pathname, Err: syscall.ENOTDIR}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all existing directories
|
||||||
|
existingPath, toCreate, err := findExistingPrefix(base, pathname)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error opening directory %s: %s", pathname, err)
|
||||||
|
}
|
||||||
|
if len(toCreate) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the existing directory is inside allowed base
|
||||||
|
fullExistingPath, err := evalSymlink(existingPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error opening existing directory %s: %s", existingPath, err)
|
||||||
|
}
|
||||||
|
fullBasePath, err := evalSymlink(base)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot read link %s: %s", base, err)
|
||||||
|
}
|
||||||
|
if !mount.PathWithinBase(fullExistingPath, fullBasePath) {
|
||||||
|
return fmt.Errorf("path %s is outside of allowed base %s", fullExistingPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// lock all intermediate directories from fullBasePath to fullExistingPath (top to bottom)
|
||||||
|
fileHandles, err := lockAndCheckSubPathWithoutSymlink(fullBasePath, fullExistingPath)
|
||||||
|
defer unlockPath(fileHandles)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.V(4).Infof("%q already exists, %q to create", fullExistingPath, filepath.Join(toCreate...))
|
||||||
|
currentPath := fullExistingPath
|
||||||
|
// create the directories one by one, making sure nobody can change
|
||||||
|
// created directory into symlink by lock that directory immediately
|
||||||
|
for _, dir := range toCreate {
|
||||||
|
currentPath = filepath.Join(currentPath, dir)
|
||||||
|
klog.V(4).Infof("Creating %s", dir)
|
||||||
|
if err := os.Mkdir(currentPath, perm); err != nil {
|
||||||
|
return fmt.Errorf("cannot create directory %s: %s", currentPath, err)
|
||||||
|
}
|
||||||
|
handle, err := lockPath(currentPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot lock path %s: %s", currentPath, err)
|
||||||
|
}
|
||||||
|
defer syscall.CloseHandle(syscall.Handle(handle))
|
||||||
|
// make sure newly created directory does not contain symlink after lock
|
||||||
|
stat, err := os.Lstat(currentPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Lstat(%q) error: %v", currentPath, err)
|
||||||
|
}
|
||||||
|
if stat.Mode()&os.ModeSymlink != 0 {
|
||||||
|
return fmt.Errorf("subpath %q is an unexpected symlink after Mkdir", currentPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// findExistingPrefix finds prefix of pathname that exists. In addition, it
|
||||||
|
// returns list of remaining directories that don't exist yet.
|
||||||
|
func findExistingPrefix(base, pathname string) (string, []string, error) {
|
||||||
|
rel, err := filepath.Rel(base, pathname)
|
||||||
|
if err != nil {
|
||||||
|
return base, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if mount.StartsWithBackstep(rel) {
|
||||||
|
return base, nil, fmt.Errorf("pathname(%s) is not within base(%s)", pathname, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rel == "." {
|
||||||
|
// base and pathname are equal
|
||||||
|
return pathname, []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dirs := strings.Split(rel, string(filepath.Separator))
|
||||||
|
|
||||||
|
var parent string
|
||||||
|
currentPath := base
|
||||||
|
for i, dir := range dirs {
|
||||||
|
parent = currentPath
|
||||||
|
currentPath = filepath.Join(parent, dir)
|
||||||
|
if _, err := os.Lstat(currentPath); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return parent, dirs[i:], nil
|
||||||
|
}
|
||||||
|
return base, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathname, []string{}, nil
|
||||||
|
}
|
||||||
295
vendor/k8s.io/kubernetes/pkg/volume/volume.go
generated
vendored
Normal file
295
vendor/k8s.io/kubernetes/pkg/volume/volume.go
generated
vendored
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package volume
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Volume represents a directory used by pods or hosts on a node. All method
|
||||||
|
// implementations of methods in the volume interface must be idempotent.
|
||||||
|
type Volume interface {
|
||||||
|
// GetPath returns the path to which the volume should be mounted for the
|
||||||
|
// pod.
|
||||||
|
GetPath() string
|
||||||
|
|
||||||
|
// MetricsProvider embeds methods for exposing metrics (e.g.
|
||||||
|
// used, available space).
|
||||||
|
MetricsProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockVolume interface provides methods to generate global map path
|
||||||
|
// and pod device map path.
|
||||||
|
type BlockVolume interface {
|
||||||
|
// GetGlobalMapPath returns a global map path which contains
|
||||||
|
// bind mount associated to a block device.
|
||||||
|
// ex. plugins/kubernetes.io/{PluginName}/{DefaultKubeletVolumeDevicesDirName}/{volumePluginDependentPath}/{pod uuid}
|
||||||
|
GetGlobalMapPath(spec *Spec) (string, error)
|
||||||
|
// GetPodDeviceMapPath returns a pod device map path
|
||||||
|
// and name of a symbolic link associated to a block device.
|
||||||
|
// ex. pods/{podUid}/{DefaultKubeletVolumeDevicesDirName}/{escapeQualifiedPluginName}/, {volumeName}
|
||||||
|
GetPodDeviceMapPath() (string, string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetricsProvider exposes metrics (e.g. used,available space) related to a
|
||||||
|
// Volume.
|
||||||
|
type MetricsProvider interface {
|
||||||
|
// GetMetrics returns the Metrics for the Volume. Maybe expensive for
|
||||||
|
// some implementations.
|
||||||
|
GetMetrics() (*Metrics, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metrics represents the used and available bytes of the Volume.
|
||||||
|
type Metrics struct {
|
||||||
|
// The time at which these stats were updated.
|
||||||
|
Time metav1.Time
|
||||||
|
|
||||||
|
// Used represents the total bytes used by the Volume.
|
||||||
|
// Note: For block devices this maybe more than the total size of the files.
|
||||||
|
Used *resource.Quantity
|
||||||
|
|
||||||
|
// Capacity represents the total capacity (bytes) of the volume's
|
||||||
|
// underlying storage. For Volumes that share a filesystem with the host
|
||||||
|
// (e.g. emptydir, hostpath) this is the size of the underlying storage,
|
||||||
|
// and will not equal Used + Available as the fs is shared.
|
||||||
|
Capacity *resource.Quantity
|
||||||
|
|
||||||
|
// Available represents the storage space available (bytes) for the
|
||||||
|
// Volume. For Volumes that share a filesystem with the host (e.g.
|
||||||
|
// emptydir, hostpath), this is the available space on the underlying
|
||||||
|
// storage, and is shared with host processes and other Volumes.
|
||||||
|
Available *resource.Quantity
|
||||||
|
|
||||||
|
// InodesUsed represents the total inodes used by the Volume.
|
||||||
|
InodesUsed *resource.Quantity
|
||||||
|
|
||||||
|
// Inodes represents the total number of inodes available in the volume.
|
||||||
|
// For volumes that share a filesystem with the host (e.g. emptydir, hostpath),
|
||||||
|
// this is the inodes available in the underlying storage,
|
||||||
|
// and will not equal InodesUsed + InodesFree as the fs is shared.
|
||||||
|
Inodes *resource.Quantity
|
||||||
|
|
||||||
|
// InodesFree represent the inodes available for the volume. For Volumes that share
|
||||||
|
// a filesystem with the host (e.g. emptydir, hostpath), this is the free inodes
|
||||||
|
// on the underlying storage, and is shared with host processes and other volumes
|
||||||
|
InodesFree *resource.Quantity
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attributes represents the attributes of this mounter.
|
||||||
|
type Attributes struct {
|
||||||
|
ReadOnly bool
|
||||||
|
Managed bool
|
||||||
|
SupportsSELinux bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// MounterArgs provides more easily extensible arguments to Mounter
|
||||||
|
type MounterArgs struct {
|
||||||
|
// When FsUser is set, the ownership of the volume will be modified to be
|
||||||
|
// owned and writable by FsUser. Otherwise, there is no side effects.
|
||||||
|
// Currently only supported with projected service account tokens.
|
||||||
|
FsUser *int64
|
||||||
|
FsGroup *int64
|
||||||
|
FSGroupChangePolicy *v1.PodFSGroupChangePolicy
|
||||||
|
DesiredSize *resource.Quantity
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mounter interface provides methods to set up/mount the volume.
|
||||||
|
type Mounter interface {
|
||||||
|
// Uses Interface to provide the path for Docker binds.
|
||||||
|
Volume
|
||||||
|
|
||||||
|
// CanMount is called immediately prior to Setup to check if
|
||||||
|
// the required components (binaries, etc.) are available on
|
||||||
|
// the underlying node to complete the subsequent SetUp (mount)
|
||||||
|
// operation. If CanMount returns error, the mount operation is
|
||||||
|
// aborted and an event is generated indicating that the node
|
||||||
|
// does not have the required binaries to complete the mount.
|
||||||
|
// If CanMount succeeds, the mount operation continues
|
||||||
|
// normally. The CanMount check can be enabled or disabled
|
||||||
|
// using the experimental-check-mount-binaries binary flag
|
||||||
|
CanMount() error
|
||||||
|
|
||||||
|
// SetUp prepares and mounts/unpacks the volume to a
|
||||||
|
// self-determined directory path. The mount point and its
|
||||||
|
// content should be owned by `fsUser` or 'fsGroup' so that it can be
|
||||||
|
// accessed by the pod. This may be called more than once, so
|
||||||
|
// implementations must be idempotent.
|
||||||
|
// It could return following types of errors:
|
||||||
|
// - TransientOperationFailure
|
||||||
|
// - UncertainProgressError
|
||||||
|
// - Error of any other type should be considered a final error
|
||||||
|
SetUp(mounterArgs MounterArgs) error
|
||||||
|
|
||||||
|
// SetUpAt prepares and mounts/unpacks the volume to the
|
||||||
|
// specified directory path, which may or may not exist yet.
|
||||||
|
// The mount point and its content should be owned by `fsUser`
|
||||||
|
// 'fsGroup' so that it can be accessed by the pod. This may
|
||||||
|
// be called more than once, so implementations must be
|
||||||
|
// idempotent.
|
||||||
|
SetUpAt(dir string, mounterArgs MounterArgs) error
|
||||||
|
// GetAttributes returns the attributes of the mounter.
|
||||||
|
// This function is called after SetUp()/SetUpAt().
|
||||||
|
GetAttributes() Attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmounter interface provides methods to cleanup/unmount the volumes.
|
||||||
|
type Unmounter interface {
|
||||||
|
Volume
|
||||||
|
// TearDown unmounts the volume from a self-determined directory and
|
||||||
|
// removes traces of the SetUp procedure.
|
||||||
|
TearDown() error
|
||||||
|
// TearDown unmounts the volume from the specified directory and
|
||||||
|
// removes traces of the SetUp procedure.
|
||||||
|
TearDownAt(dir string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockVolumeMapper interface is a mapper interface for block volume.
|
||||||
|
type BlockVolumeMapper interface {
|
||||||
|
BlockVolume
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomBlockVolumeMapper interface provides custom methods to set up/map the volume.
|
||||||
|
type CustomBlockVolumeMapper interface {
|
||||||
|
BlockVolumeMapper
|
||||||
|
// SetUpDevice prepares the volume to the node by the plugin specific way.
|
||||||
|
// For most in-tree plugins, attacher.Attach() and attacher.WaitForAttach()
|
||||||
|
// will do necessary works.
|
||||||
|
// This may be called more than once, so implementations must be idempotent.
|
||||||
|
// SetUpDevice returns stagingPath if device setup was successful
|
||||||
|
SetUpDevice() (stagingPath string, err error)
|
||||||
|
|
||||||
|
// MapPodDevice maps the block device to a path and return the path.
|
||||||
|
// Unique device path across kubelet node reboot is required to avoid
|
||||||
|
// unexpected block volume destruction.
|
||||||
|
// If empty string is returned, the path retuned by attacher.Attach() and
|
||||||
|
// attacher.WaitForAttach() will be used.
|
||||||
|
MapPodDevice() (publishPath string, err error)
|
||||||
|
|
||||||
|
// GetStagingPath returns path that was used for staging the volume
|
||||||
|
// it is mainly used by CSI plugins
|
||||||
|
GetStagingPath() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockVolumeUnmapper interface is an unmapper interface for block volume.
|
||||||
|
type BlockVolumeUnmapper interface {
|
||||||
|
BlockVolume
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomBlockVolumeUnmapper interface provides custom methods to cleanup/unmap the volumes.
|
||||||
|
type CustomBlockVolumeUnmapper interface {
|
||||||
|
BlockVolumeUnmapper
|
||||||
|
// TearDownDevice removes traces of the SetUpDevice procedure.
|
||||||
|
// If the plugin is non-attachable, this method detaches the volume
|
||||||
|
// from a node.
|
||||||
|
TearDownDevice(mapPath string, devicePath string) error
|
||||||
|
|
||||||
|
// UnmapPodDevice removes traces of the MapPodDevice procedure.
|
||||||
|
UnmapPodDevice() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provisioner is an interface that creates templates for PersistentVolumes
|
||||||
|
// and can create the volume as a new resource in the infrastructure provider.
|
||||||
|
type Provisioner interface {
|
||||||
|
// Provision creates the resource by allocating the underlying volume in a
|
||||||
|
// storage system. This method should block until completion and returns
|
||||||
|
// PersistentVolume representing the created storage resource.
|
||||||
|
Provision(selectedNode *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (*v1.PersistentVolume, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deleter removes the resource from the underlying storage provider. Calls
|
||||||
|
// to this method should block until the deletion is complete. Any error
|
||||||
|
// returned indicates the volume has failed to be reclaimed. A nil return
|
||||||
|
// indicates success.
|
||||||
|
type Deleter interface {
|
||||||
|
Volume
|
||||||
|
// This method should block until completion.
|
||||||
|
// deletedVolumeInUseError returned from this function will not be reported
|
||||||
|
// as error and it will be sent as "Info" event to the PV being deleted. The
|
||||||
|
// volume controller will retry deleting the volume in the next periodic
|
||||||
|
// sync. This can be used to postpone deletion of a volume that is being
|
||||||
|
// detached from a node. Deletion of such volume would fail anyway and such
|
||||||
|
// error would confuse users.
|
||||||
|
Delete() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attacher can attach a volume to a node.
|
||||||
|
type Attacher interface {
|
||||||
|
DeviceMounter
|
||||||
|
|
||||||
|
// Attaches the volume specified by the given spec to the node with the given Name.
|
||||||
|
// On success, returns the device path where the device was attached on the
|
||||||
|
// node.
|
||||||
|
Attach(spec *Spec, nodeName types.NodeName) (string, error)
|
||||||
|
|
||||||
|
// VolumesAreAttached checks whether the list of volumes still attached to the specified
|
||||||
|
// node. It returns a map which maps from the volume spec to the checking result.
|
||||||
|
// If an error is occurred during checking, the error will be returned
|
||||||
|
VolumesAreAttached(specs []*Spec, nodeName types.NodeName) (map[*Spec]bool, error)
|
||||||
|
|
||||||
|
// WaitForAttach blocks until the device is attached to this
|
||||||
|
// node. If it successfully attaches, the path to the device
|
||||||
|
// is returned. Otherwise, if the device does not attach after
|
||||||
|
// the given timeout period, an error will be returned.
|
||||||
|
WaitForAttach(spec *Spec, devicePath string, pod *v1.Pod, timeout time.Duration) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceMounter can mount a block volume to a global path.
|
||||||
|
type DeviceMounter interface {
|
||||||
|
// GetDeviceMountPath returns a path where the device should
|
||||||
|
// be mounted after it is attached. This is a global mount
|
||||||
|
// point which should be bind mounted for individual volumes.
|
||||||
|
GetDeviceMountPath(spec *Spec) (string, error)
|
||||||
|
|
||||||
|
// MountDevice mounts the disk to a global path which
|
||||||
|
// individual pods can then bind mount
|
||||||
|
// Note that devicePath can be empty if the volume plugin does not implement any of Attach and WaitForAttach methods.
|
||||||
|
// It could return following types of errors:
|
||||||
|
// - TransientOperationFailure
|
||||||
|
// - UncertainProgressError
|
||||||
|
// - Error of any other type should be considered a final error
|
||||||
|
MountDevice(spec *Spec, devicePath string, deviceMountPath string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type BulkVolumeVerifier interface {
|
||||||
|
// BulkVerifyVolumes checks whether the list of volumes still attached to the
|
||||||
|
// the clusters in the node. It returns a map which maps from the volume spec to the checking result.
|
||||||
|
// If an error occurs during check - error should be returned and volume on nodes
|
||||||
|
// should be assumed as still attached.
|
||||||
|
BulkVerifyVolumes(volumesByNode map[types.NodeName][]*Spec) (map[types.NodeName]map[*Spec]bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detacher can detach a volume from a node.
|
||||||
|
type Detacher interface {
|
||||||
|
DeviceUnmounter
|
||||||
|
// Detach the given volume from the node with the given Name.
|
||||||
|
// volumeName is name of the volume as returned from plugin's
|
||||||
|
// GetVolumeName().
|
||||||
|
Detach(volumeName string, nodeName types.NodeName) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceUnmounter can unmount a block volume from the global path.
|
||||||
|
type DeviceUnmounter interface {
|
||||||
|
// UnmountDevice unmounts the global mount of the disk. This
|
||||||
|
// should only be called once all bind mounts have been
|
||||||
|
// unmounted.
|
||||||
|
UnmountDevice(deviceMountPath string) error
|
||||||
|
}
|
||||||
241
vendor/k8s.io/kubernetes/pkg/volume/volume_linux.go
generated
vendored
Normal file
241
vendor/k8s.io/kubernetes/pkg/volume/volume_linux.go
generated
vendored
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package volume
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
rwMask = os.FileMode(0660)
|
||||||
|
roMask = os.FileMode(0440)
|
||||||
|
execMask = os.FileMode(0110)
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetVolumeOwnership modifies the given volume to be owned by
|
||||||
|
// fsGroup, and sets SetGid so that newly created files are owned by
|
||||||
|
// fsGroup. If fsGroup is nil nothing is done.
|
||||||
|
func SetVolumeOwnership(mounter Mounter, fsGroup *int64, fsGroupChangePolicy *v1.PodFSGroupChangePolicy, completeFunc func(*error)) error {
|
||||||
|
if fsGroup == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fsGroupPolicyEnabled := utilfeature.DefaultFeatureGate.Enabled(features.ConfigurableFSGroupPolicy)
|
||||||
|
|
||||||
|
timer := time.AfterFunc(30*time.Second, func() {
|
||||||
|
klog.Warningf("Setting volume ownership for %s and fsGroup set. If the volume has a lot of files then setting volume ownership could be slow, see https://github.com/kubernetes/kubernetes/issues/69699", mounter.GetPath())
|
||||||
|
})
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
|
// This code exists for legacy purposes, so as old behaviour is entirely preserved when feature gate is disabled
|
||||||
|
// TODO: remove this when ConfigurableFSGroupPolicy turns GA.
|
||||||
|
if !fsGroupPolicyEnabled {
|
||||||
|
err := legacyOwnershipChange(mounter, fsGroup)
|
||||||
|
if completeFunc != nil {
|
||||||
|
completeFunc(&err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if skipPermissionChange(mounter, fsGroup, fsGroupChangePolicy) {
|
||||||
|
klog.V(3).Infof("skipping permission and ownership change for volume %s", mounter.GetPath())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := walkDeep(mounter.GetPath(), func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return changeFilePermission(path, fsGroup, mounter.GetAttributes().ReadOnly, info)
|
||||||
|
})
|
||||||
|
if completeFunc != nil {
|
||||||
|
completeFunc(&err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func legacyOwnershipChange(mounter Mounter, fsGroup *int64) error {
|
||||||
|
return filepath.Walk(mounter.GetPath(), func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return changeFilePermission(path, fsGroup, mounter.GetAttributes().ReadOnly, info)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func changeFilePermission(filename string, fsGroup *int64, readonly bool, info os.FileInfo) error {
|
||||||
|
// chown and chmod pass through to the underlying file for symlinks.
|
||||||
|
// Symlinks have a mode of 777 but this really doesn't mean anything.
|
||||||
|
// The permissions of the underlying file are what matter.
|
||||||
|
// However, if one reads the mode of a symlink then chmods the symlink
|
||||||
|
// with that mode, it changes the mode of the underlying file, overridden
|
||||||
|
// the defaultMode and permissions initialized by the volume plugin, which
|
||||||
|
// is not what we want; thus, we skip chown/chmod for symlinks.
|
||||||
|
if info.Mode()&os.ModeSymlink != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if stat == nil {
|
||||||
|
klog.Errorf("Got nil stat_t for path %v while setting ownership of volume", filename)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := os.Chown(filename, int(stat.Uid), int(*fsGroup))
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Chown failed on %v: %v", filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mask := rwMask
|
||||||
|
if readonly {
|
||||||
|
mask = roMask
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
mask |= os.ModeSetgid
|
||||||
|
mask |= execMask
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Chmod(filename, info.Mode()|mask)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Chmod failed on %v: %v", filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipPermissionChange(mounter Mounter, fsGroup *int64, fsGroupChangePolicy *v1.PodFSGroupChangePolicy) bool {
|
||||||
|
dir := mounter.GetPath()
|
||||||
|
|
||||||
|
if fsGroupChangePolicy == nil || *fsGroupChangePolicy != v1.FSGroupChangeOnRootMismatch {
|
||||||
|
klog.V(4).Infof("perform recursive ownership change for %s", dir)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !requiresPermissionChange(mounter.GetPath(), fsGroup, mounter.GetAttributes().ReadOnly)
|
||||||
|
}
|
||||||
|
|
||||||
|
func requiresPermissionChange(rootDir string, fsGroup *int64, readonly bool) bool {
|
||||||
|
fsInfo, err := os.Stat(rootDir)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("performing recursive ownership change on %s because reading permissions of root volume failed: %v", rootDir, err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
stat, ok := fsInfo.Sys().(*syscall.Stat_t)
|
||||||
|
if !ok || stat == nil {
|
||||||
|
klog.Errorf("performing recursive ownership change on %s because reading permissions of root volume failed", rootDir)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(stat.Gid) != int(*fsGroup) {
|
||||||
|
klog.V(4).Infof("expected group ownership of volume %s did not match with: %d", rootDir, stat.Gid)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
unixPerms := rwMask
|
||||||
|
|
||||||
|
if readonly {
|
||||||
|
unixPerms = roMask
|
||||||
|
}
|
||||||
|
|
||||||
|
// if rootDir is not a directory then we should apply permission change anyways
|
||||||
|
if !fsInfo.IsDir() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
unixPerms |= execMask
|
||||||
|
filePerm := fsInfo.Mode().Perm()
|
||||||
|
|
||||||
|
// We need to check if actual permissions of root directory is a superset of permissions required by unixPerms.
|
||||||
|
// This is done by checking if permission bits expected in unixPerms is set in actual permissions of the directory.
|
||||||
|
// We use bitwise AND operation to check set bits. For example:
|
||||||
|
// unixPerms: 770, filePerms: 775 : 770&775 = 770 (perms on directory is a superset)
|
||||||
|
// unixPerms: 770, filePerms: 770 : 770&770 = 770 (perms on directory is a superset)
|
||||||
|
// unixPerms: 770, filePerms: 750 : 770&750 = 750 (perms on directory is NOT a superset)
|
||||||
|
// We also need to check if setgid bits are set in permissions of the directory.
|
||||||
|
if (unixPerms&filePerm != unixPerms) || (fsInfo.Mode()&os.ModeSetgid == 0) {
|
||||||
|
klog.V(4).Infof("performing recursive ownership change on %s because of mismatching mode", rootDir)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// readDirNames reads the directory named by dirname and returns
|
||||||
|
// a list of directory entries.
|
||||||
|
// We are not using filepath.readDirNames because we do not want to sort files found in a directory before changing
|
||||||
|
// permissions for performance reasons.
|
||||||
|
func readDirNames(dirname string) ([]string, error) {
|
||||||
|
f, err := os.Open(dirname)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
names, err := f.Readdirnames(-1)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkDeep can be used to traverse directories and has two minor differences
|
||||||
|
// from filepath.Walk:
|
||||||
|
// - List of files/dirs is not sorted for performance reasons
|
||||||
|
// - callback walkFunc is invoked on root directory after visiting children dirs and files
|
||||||
|
func walkDeep(root string, walkFunc filepath.WalkFunc) error {
|
||||||
|
info, err := os.Lstat(root)
|
||||||
|
if err != nil {
|
||||||
|
return walkFunc(root, nil, err)
|
||||||
|
}
|
||||||
|
return walk(root, info, walkFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func walk(path string, info os.FileInfo, walkFunc filepath.WalkFunc) error {
|
||||||
|
if !info.IsDir() {
|
||||||
|
return walkFunc(path, info, nil)
|
||||||
|
}
|
||||||
|
names, err := readDirNames(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, name := range names {
|
||||||
|
filename := filepath.Join(path, name)
|
||||||
|
fileInfo, err := os.Lstat(filename)
|
||||||
|
if err != nil {
|
||||||
|
if err := walkFunc(filename, fileInfo, err); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = walk(filename, fileInfo, walkFunc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return walkFunc(path, info, nil)
|
||||||
|
}
|
||||||
27
vendor/k8s.io/kubernetes/pkg/volume/volume_unsupported.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/pkg/volume/volume_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// +build !linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package volume
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetVolumeOwnership(mounter Mounter, fsGroup *int64, fsGroupChangePolicy *v1.PodFSGroupChangePolicy, completeFunc func(*error)) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
201
vendor/k8s.io/mount-utils/LICENSE
generated
vendored
Normal file
201
vendor/k8s.io/mount-utils/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
14
vendor/k8s.io/mount-utils/OWNERS
generated
vendored
Normal file
14
vendor/k8s.io/mount-utils/OWNERS
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# See the OWNERS docs at https://go.k8s.io/owners
|
||||||
|
|
||||||
|
reviewers:
|
||||||
|
- jingxu97
|
||||||
|
- saad-ali
|
||||||
|
- jsafrane
|
||||||
|
- msau42
|
||||||
|
- andyzhangx
|
||||||
|
- gnufied
|
||||||
|
approvers:
|
||||||
|
- jingxu97
|
||||||
|
- saad-ali
|
||||||
|
- jsafrane
|
||||||
|
|
||||||
30
vendor/k8s.io/mount-utils/README.md
generated
vendored
Normal file
30
vendor/k8s.io/mount-utils/README.md
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
## Purpose
|
||||||
|
|
||||||
|
This repository defines an interface to mounting filesystems to be consumed by
|
||||||
|
various Kubernetes and out-of-tree CSI components.
|
||||||
|
|
||||||
|
Consumers of this repository can make use of functions like 'Mount' to mount
|
||||||
|
source to target as fstype with given options, 'Unmount' to unmount a target.
|
||||||
|
Other useful functions include 'List' all mounted file systems and find all
|
||||||
|
mount references to a path using 'GetMountRefs'
|
||||||
|
|
||||||
|
## Community, discussion, contribution, and support
|
||||||
|
|
||||||
|
Learn how to engage with the Kubernetes community on the [community
|
||||||
|
page](http://kubernetes.io/community/).
|
||||||
|
|
||||||
|
You can reach the maintainers of this repository at:
|
||||||
|
|
||||||
|
- Slack: #sig-storage (on https://kubernetes.slack.com -- get an
|
||||||
|
invite at slack.kubernetes.io)
|
||||||
|
- Mailing List:
|
||||||
|
https://groups.google.com/forum/#!forum/kubernetes-sig-storage
|
||||||
|
|
||||||
|
### Code of Conduct
|
||||||
|
|
||||||
|
Participation in the Kubernetes community is governed by the [Kubernetes
|
||||||
|
Code of Conduct](code-of-conduct.md).
|
||||||
|
|
||||||
|
### Contibution Guidelines
|
||||||
|
|
||||||
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for more information.
|
||||||
18
vendor/k8s.io/mount-utils/SECURITY_CONTACTS
generated
vendored
Normal file
18
vendor/k8s.io/mount-utils/SECURITY_CONTACTS
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Defined below are the security contacts for this repo.
|
||||||
|
#
|
||||||
|
# They are the contact point for the Product Security Committee to reach out
|
||||||
|
# to for triaging and handling of incoming issues.
|
||||||
|
#
|
||||||
|
# The below names agree to abide by the
|
||||||
|
# [Embargo Policy](https://git.k8s.io/security/private-distributors-list.md#embargo-policy)
|
||||||
|
# and will be removed and replaced if they violate that agreement.
|
||||||
|
#
|
||||||
|
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
|
||||||
|
# INSTRUCTIONS AT https://kubernetes.io/security/
|
||||||
|
|
||||||
|
saad-ali
|
||||||
|
cjcullen
|
||||||
|
joelsmith
|
||||||
|
liggitt
|
||||||
|
philips
|
||||||
|
tallclair
|
||||||
3
vendor/k8s.io/mount-utils/code-of-conduct.md
generated
vendored
Normal file
3
vendor/k8s.io/mount-utils/code-of-conduct.md
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Kubernetes Community Code of Conduct
|
||||||
|
|
||||||
|
Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)
|
||||||
18
vendor/k8s.io/mount-utils/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/mount-utils/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package mount defines an interface to mounting filesystems.
|
||||||
|
package mount // import "k8s.io/mount-utils"
|
||||||
220
vendor/k8s.io/mount-utils/fake_mounter.go
generated
vendored
Normal file
220
vendor/k8s.io/mount-utils/fake_mounter.go
generated
vendored
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FakeMounter implements mount.Interface for tests.
|
||||||
|
type FakeMounter struct {
|
||||||
|
MountPoints []MountPoint
|
||||||
|
log []FakeAction
|
||||||
|
// Error to return for a path when calling IsLikelyNotMountPoint
|
||||||
|
MountCheckErrors map[string]error
|
||||||
|
// Some tests run things in parallel, make sure the mounter does not produce
|
||||||
|
// any golang's DATA RACE warnings.
|
||||||
|
mutex sync.Mutex
|
||||||
|
UnmountFunc UnmountFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmountFunc is a function callback to be executed during the Unmount() call.
|
||||||
|
type UnmountFunc func(path string) error
|
||||||
|
|
||||||
|
var _ Interface = &FakeMounter{}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FakeActionMount is the string for specifying mount as FakeAction.Action
|
||||||
|
FakeActionMount = "mount"
|
||||||
|
// FakeActionUnmount is the string for specifying unmount as FakeAction.Action
|
||||||
|
FakeActionUnmount = "unmount"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FakeAction objects are logged every time a fake mount or unmount is called.
|
||||||
|
type FakeAction struct {
|
||||||
|
Action string // "mount" or "unmount"
|
||||||
|
Target string // applies to both mount and unmount actions
|
||||||
|
Source string // applies only to "mount" actions
|
||||||
|
FSType string // applies only to "mount" actions
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFakeMounter returns a FakeMounter struct that implements Interface and is
|
||||||
|
// suitable for testing purposes.
|
||||||
|
func NewFakeMounter(mps []MountPoint) *FakeMounter {
|
||||||
|
return &FakeMounter{
|
||||||
|
MountPoints: mps,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetLog clears all the log entries in FakeMounter
|
||||||
|
func (f *FakeMounter) ResetLog() {
|
||||||
|
f.mutex.Lock()
|
||||||
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
|
f.log = []FakeAction{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLog returns the slice of FakeActions taken by the mounter
|
||||||
|
func (f *FakeMounter) GetLog() []FakeAction {
|
||||||
|
f.mutex.Lock()
|
||||||
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
|
return f.log
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount records the mount event and updates the in-memory mount points for FakeMounter
|
||||||
|
func (f *FakeMounter) Mount(source string, target string, fstype string, options []string) error {
|
||||||
|
return f.MountSensitive(source, target, fstype, options, nil /* sensitiveOptions */)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount records the mount event and updates the in-memory mount points for FakeMounter
|
||||||
|
// sensitiveOptions to be passed in a separate parameter from the normal
|
||||||
|
// mount options and ensures the sensitiveOptions are never logged. This
|
||||||
|
// method should be used by callers that pass sensitive material (like
|
||||||
|
// passwords) as mount options.
|
||||||
|
func (f *FakeMounter) MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
||||||
|
f.mutex.Lock()
|
||||||
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
|
opts := []string{}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
// find 'bind' option
|
||||||
|
if option == "bind" {
|
||||||
|
// This is a bind-mount. In order to mimic linux behaviour, we must
|
||||||
|
// use the original device of the bind-mount as the real source.
|
||||||
|
// E.g. when mounted /dev/sda like this:
|
||||||
|
// $ mount /dev/sda /mnt/test
|
||||||
|
// $ mount -o bind /mnt/test /mnt/bound
|
||||||
|
// then /proc/mount contains:
|
||||||
|
// /dev/sda /mnt/test
|
||||||
|
// /dev/sda /mnt/bound
|
||||||
|
// (and not /mnt/test /mnt/bound)
|
||||||
|
// I.e. we must use /dev/sda as source instead of /mnt/test in the
|
||||||
|
// bind mount.
|
||||||
|
for _, mnt := range f.MountPoints {
|
||||||
|
if source == mnt.Path {
|
||||||
|
source = mnt.Device
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// reuse MountPoint.Opts field to mark mount as readonly
|
||||||
|
opts = append(opts, option)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If target is a symlink, get its absolute path
|
||||||
|
absTarget, err := filepath.EvalSymlinks(target)
|
||||||
|
if err != nil {
|
||||||
|
absTarget = target
|
||||||
|
}
|
||||||
|
f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: absTarget, Type: fstype, Opts: append(opts, sensitiveOptions...)})
|
||||||
|
klog.V(5).Infof("Fake mounter: mounted %s to %s", source, absTarget)
|
||||||
|
f.log = append(f.log, FakeAction{Action: FakeActionMount, Target: absTarget, Source: source, FSType: fstype})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FakeMounter) MountSensitiveWithoutSystemd(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
||||||
|
return f.MountSensitive(source, target, fstype, options, nil /* sensitiveOptions */)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmount records the unmount event and updates the in-memory mount points for FakeMounter
|
||||||
|
func (f *FakeMounter) Unmount(target string) error {
|
||||||
|
f.mutex.Lock()
|
||||||
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
|
// If target is a symlink, get its absolute path
|
||||||
|
absTarget, err := filepath.EvalSymlinks(target)
|
||||||
|
if err != nil {
|
||||||
|
absTarget = target
|
||||||
|
}
|
||||||
|
|
||||||
|
newMountpoints := []MountPoint{}
|
||||||
|
for _, mp := range f.MountPoints {
|
||||||
|
if mp.Path == absTarget {
|
||||||
|
if f.UnmountFunc != nil {
|
||||||
|
err := f.UnmountFunc(absTarget)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
klog.V(5).Infof("Fake mounter: unmounted %s from %s", mp.Device, absTarget)
|
||||||
|
// Don't copy it to newMountpoints
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newMountpoints = append(newMountpoints, MountPoint{Device: mp.Device, Path: mp.Path, Type: mp.Type})
|
||||||
|
}
|
||||||
|
f.MountPoints = newMountpoints
|
||||||
|
f.log = append(f.log, FakeAction{Action: FakeActionUnmount, Target: absTarget})
|
||||||
|
delete(f.MountCheckErrors, target)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns all the in-memory mountpoints for FakeMounter
|
||||||
|
func (f *FakeMounter) List() ([]MountPoint, error) {
|
||||||
|
f.mutex.Lock()
|
||||||
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
|
return f.MountPoints, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLikelyNotMountPoint determines whether a path is a mountpoint by checking
|
||||||
|
// if the absolute path to file is in the in-memory mountpoints
|
||||||
|
func (f *FakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||||
|
f.mutex.Lock()
|
||||||
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
|
err := f.MountCheckErrors[file]
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = os.Stat(file)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If file is a symlink, get its absolute path
|
||||||
|
absFile, err := filepath.EvalSymlinks(file)
|
||||||
|
if err != nil {
|
||||||
|
absFile = file
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mp := range f.MountPoints {
|
||||||
|
if mp.Path == absFile {
|
||||||
|
klog.V(5).Infof("isLikelyNotMountPoint for %s: mounted %s, false", file, mp.Path)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
klog.V(5).Infof("isLikelyNotMountPoint for %s: true", file)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMountRefs finds all mount references to the path, returns a
|
||||||
|
// list of paths.
|
||||||
|
func (f *FakeMounter) GetMountRefs(pathname string) ([]string, error) {
|
||||||
|
realpath, err := filepath.EvalSymlinks(pathname)
|
||||||
|
if err != nil {
|
||||||
|
// Ignore error in FakeMounter, because we actually didn't create files.
|
||||||
|
realpath = pathname
|
||||||
|
}
|
||||||
|
return getMountRefsByDev(f, realpath)
|
||||||
|
}
|
||||||
13
vendor/k8s.io/mount-utils/go.mod
generated
vendored
Normal file
13
vendor/k8s.io/mount-utils/go.mod
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// This is a generated file. Do not edit directly.
|
||||||
|
|
||||||
|
module k8s.io/mount-utils
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/kr/pretty v0.2.0 // indirect
|
||||||
|
github.com/stretchr/testify v1.6.1
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
|
k8s.io/klog/v2 v2.4.0
|
||||||
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920
|
||||||
|
)
|
||||||
32
vendor/k8s.io/mount-utils/go.sum
generated
vendored
Normal file
32
vendor/k8s.io/mount-utils/go.sum
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
|
||||||
|
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||||
|
github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY=
|
||||||
|
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||||
|
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||||
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||||
|
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
k8s.io/klog/v2 v2.0.0 h1:Foj74zO6RbjjP4hBEKjnYtjjAhGg4jNynUdYF6fJrok=
|
||||||
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
|
k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
|
||||||
|
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||||
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
|
||||||
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||||
368
vendor/k8s.io/mount-utils/mount.go
generated
vendored
Normal file
368
vendor/k8s.io/mount-utils/mount.go
generated
vendored
Normal file
@ -0,0 +1,368 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO(thockin): This whole pkg is pretty linux-centric. As soon as we have
|
||||||
|
// an alternate platform, we will need to abstract further.
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
utilexec "k8s.io/utils/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Default mount command if mounter path is not specified.
|
||||||
|
defaultMountCommand = "mount"
|
||||||
|
// Log message where sensitive mount options were removed
|
||||||
|
sensitiveOptionsRemoved = "<masked>"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface defines the set of methods to allow for mount operations on a system.
|
||||||
|
type Interface interface {
|
||||||
|
// Mount mounts source to target as fstype with given options.
|
||||||
|
// options MUST not contain sensitive material (like passwords).
|
||||||
|
Mount(source string, target string, fstype string, options []string) error
|
||||||
|
// MountSensitive is the same as Mount() but this method allows
|
||||||
|
// sensitiveOptions to be passed in a separate parameter from the normal
|
||||||
|
// mount options and ensures the sensitiveOptions are never logged. This
|
||||||
|
// method should be used by callers that pass sensitive material (like
|
||||||
|
// passwords) as mount options.
|
||||||
|
MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error
|
||||||
|
// MountSensitiveWithoutSystemd is the same as MountSensitive() but this method disable using systemd mount.
|
||||||
|
MountSensitiveWithoutSystemd(source string, target string, fstype string, options []string, sensitiveOptions []string) error
|
||||||
|
// Unmount unmounts given target.
|
||||||
|
Unmount(target string) error
|
||||||
|
// List returns a list of all mounted filesystems. This can be large.
|
||||||
|
// On some platforms, reading mounts directly from the OS is not guaranteed
|
||||||
|
// consistent (i.e. it could change between chunked reads). This is guaranteed
|
||||||
|
// to be consistent.
|
||||||
|
List() ([]MountPoint, error)
|
||||||
|
// IsLikelyNotMountPoint uses heuristics to determine if a directory
|
||||||
|
// is not a mountpoint.
|
||||||
|
// It should return ErrNotExist when the directory does not exist.
|
||||||
|
// IsLikelyNotMountPoint does NOT properly detect all mountpoint types
|
||||||
|
// most notably linux bind mounts and symbolic link. For callers that do not
|
||||||
|
// care about such situations, this is a faster alternative to calling List()
|
||||||
|
// and scanning that output.
|
||||||
|
IsLikelyNotMountPoint(file string) (bool, error)
|
||||||
|
// GetMountRefs finds all mount references to pathname, returning a slice of
|
||||||
|
// paths. Pathname can be a mountpoint path or a normal directory
|
||||||
|
// (for bind mount). On Linux, pathname is excluded from the slice.
|
||||||
|
// For example, if /dev/sdc was mounted at /path/a and /path/b,
|
||||||
|
// GetMountRefs("/path/a") would return ["/path/b"]
|
||||||
|
// GetMountRefs("/path/b") would return ["/path/a"]
|
||||||
|
// On Windows there is no way to query all mount points; as long as pathname is
|
||||||
|
// a valid mount, it will be returned.
|
||||||
|
GetMountRefs(pathname string) ([]string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile-time check to ensure all Mounter implementations satisfy
|
||||||
|
// the mount interface.
|
||||||
|
var _ Interface = &Mounter{}
|
||||||
|
|
||||||
|
// MountPoint represents a single line in /proc/mounts or /etc/fstab.
|
||||||
|
type MountPoint struct { // nolint: golint
|
||||||
|
Device string
|
||||||
|
Path string
|
||||||
|
Type string
|
||||||
|
Opts []string // Opts may contain sensitive mount options (like passwords) and MUST be treated as such (e.g. not logged).
|
||||||
|
Freq int
|
||||||
|
Pass int
|
||||||
|
}
|
||||||
|
|
||||||
|
type MountErrorType string // nolint: golint
|
||||||
|
|
||||||
|
const (
|
||||||
|
FilesystemMismatch MountErrorType = "FilesystemMismatch"
|
||||||
|
HasFilesystemErrors MountErrorType = "HasFilesystemErrors"
|
||||||
|
UnformattedReadOnly MountErrorType = "UnformattedReadOnly"
|
||||||
|
FormatFailed MountErrorType = "FormatFailed"
|
||||||
|
GetDiskFormatFailed MountErrorType = "GetDiskFormatFailed"
|
||||||
|
UnknownMountError MountErrorType = "UnknownMountError"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MountError struct { // nolint: golint
|
||||||
|
Type MountErrorType
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mountError MountError) String() string {
|
||||||
|
return mountError.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mountError MountError) Error() string {
|
||||||
|
return mountError.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMountError(mountErrorValue MountErrorType, format string, args ...interface{}) error {
|
||||||
|
mountError := MountError{
|
||||||
|
Type: mountErrorValue,
|
||||||
|
Message: fmt.Sprintf(format, args...),
|
||||||
|
}
|
||||||
|
return mountError
|
||||||
|
}
|
||||||
|
|
||||||
|
// SafeFormatAndMount probes a device to see if it is formatted.
|
||||||
|
// Namely it checks to see if a file system is present. If so it
|
||||||
|
// mounts it otherwise the device is formatted first then mounted.
|
||||||
|
type SafeFormatAndMount struct {
|
||||||
|
Interface
|
||||||
|
Exec utilexec.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatAndMount formats the given disk, if needed, and mounts it.
|
||||||
|
// That is if the disk is not formatted and it is not being mounted as
|
||||||
|
// read-only it will format it first then mount it. Otherwise, if the
|
||||||
|
// disk is already formatted or it is being mounted as read-only, it
|
||||||
|
// will be mounted without formatting.
|
||||||
|
// options MUST not contain sensitive material (like passwords).
|
||||||
|
func (mounter *SafeFormatAndMount) FormatAndMount(source string, target string, fstype string, options []string) error {
|
||||||
|
return mounter.FormatAndMountSensitive(source, target, fstype, options, nil /* sensitiveOptions */)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatAndMountSensitive is the same as FormatAndMount but this method allows
|
||||||
|
// sensitiveOptions to be passed in a separate parameter from the normal mount
|
||||||
|
// options and ensures the sensitiveOptions are never logged. This method should
|
||||||
|
// be used by callers that pass sensitive material (like passwords) as mount
|
||||||
|
// options.
|
||||||
|
func (mounter *SafeFormatAndMount) FormatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
||||||
|
return mounter.formatAndMountSensitive(source, target, fstype, options, sensitiveOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMountRefsByDev finds all references to the device provided
|
||||||
|
// by mountPath; returns a list of paths.
|
||||||
|
// Note that mountPath should be path after the evaluation of any symblolic links.
|
||||||
|
func getMountRefsByDev(mounter Interface, mountPath string) ([]string, error) {
|
||||||
|
mps, err := mounter.List()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finding the device mounted to mountPath.
|
||||||
|
diskDev := ""
|
||||||
|
for i := range mps {
|
||||||
|
if mountPath == mps[i].Path {
|
||||||
|
diskDev = mps[i].Device
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all references to the device.
|
||||||
|
var refs []string
|
||||||
|
for i := range mps {
|
||||||
|
if mps[i].Device == diskDev || mps[i].Device == mountPath {
|
||||||
|
if mps[i].Path != mountPath {
|
||||||
|
refs = append(refs, mps[i].Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return refs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDeviceNameFromMount given a mnt point, find the device from /proc/mounts
|
||||||
|
// returns the device name, reference count, and error code.
|
||||||
|
func GetDeviceNameFromMount(mounter Interface, mountPath string) (string, int, error) {
|
||||||
|
mps, err := mounter.List()
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the device name.
|
||||||
|
// FIXME if multiple devices mounted on the same mount path, only the first one is returned.
|
||||||
|
device := ""
|
||||||
|
// If mountPath is symlink, need get its target path.
|
||||||
|
slTarget, err := filepath.EvalSymlinks(mountPath)
|
||||||
|
if err != nil {
|
||||||
|
slTarget = mountPath
|
||||||
|
}
|
||||||
|
for i := range mps {
|
||||||
|
if mps[i].Path == slTarget {
|
||||||
|
device = mps[i].Device
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all references to the device.
|
||||||
|
refCount := 0
|
||||||
|
for i := range mps {
|
||||||
|
if mps[i].Device == device {
|
||||||
|
refCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return device, refCount, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotMountPoint determines if a directory is a mountpoint.
|
||||||
|
// It should return ErrNotExist when the directory does not exist.
|
||||||
|
// IsNotMountPoint is more expensive than IsLikelyNotMountPoint.
|
||||||
|
// IsNotMountPoint detects bind mounts in linux.
|
||||||
|
// IsNotMountPoint enumerates all the mountpoints using List() and
|
||||||
|
// the list of mountpoints may be large, then it uses
|
||||||
|
// isMountPointMatch to evaluate whether the directory is a mountpoint.
|
||||||
|
func IsNotMountPoint(mounter Interface, file string) (bool, error) {
|
||||||
|
// IsLikelyNotMountPoint provides a quick check
|
||||||
|
// to determine whether file IS A mountpoint.
|
||||||
|
notMnt, notMntErr := mounter.IsLikelyNotMountPoint(file)
|
||||||
|
if notMntErr != nil && os.IsPermission(notMntErr) {
|
||||||
|
// We were not allowed to do the simple stat() check, e.g. on NFS with
|
||||||
|
// root_squash. Fall back to /proc/mounts check below.
|
||||||
|
notMnt = true
|
||||||
|
notMntErr = nil
|
||||||
|
}
|
||||||
|
if notMntErr != nil {
|
||||||
|
return notMnt, notMntErr
|
||||||
|
}
|
||||||
|
// identified as mountpoint, so return this fact.
|
||||||
|
if notMnt == false {
|
||||||
|
return notMnt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve any symlinks in file, kernel would do the same and use the resolved path in /proc/mounts.
|
||||||
|
resolvedFile, err := filepath.EvalSymlinks(file)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check all mountpoints since IsLikelyNotMountPoint
|
||||||
|
// is not reliable for some mountpoint types.
|
||||||
|
mountPoints, mountPointsErr := mounter.List()
|
||||||
|
if mountPointsErr != nil {
|
||||||
|
return notMnt, mountPointsErr
|
||||||
|
}
|
||||||
|
for _, mp := range mountPoints {
|
||||||
|
if isMountPointMatch(mp, resolvedFile) {
|
||||||
|
notMnt = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return notMnt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeBindOpts detects whether a bind mount is being requested and makes the remount options to
|
||||||
|
// use in case of bind mount, due to the fact that bind mount doesn't respect mount options.
|
||||||
|
// The list equals:
|
||||||
|
// options - 'bind' + 'remount' (no duplicate)
|
||||||
|
func MakeBindOpts(options []string) (bool, []string, []string) {
|
||||||
|
bind, bindOpts, bindRemountOpts, _ := MakeBindOptsSensitive(options, nil /* sensitiveOptions */)
|
||||||
|
return bind, bindOpts, bindRemountOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeBindOptsSensitive is the same as MakeBindOpts but this method allows
|
||||||
|
// sensitiveOptions to be passed in a separate parameter from the normal mount
|
||||||
|
// options and ensures the sensitiveOptions are never logged. This method should
|
||||||
|
// be used by callers that pass sensitive material (like passwords) as mount
|
||||||
|
// options.
|
||||||
|
func MakeBindOptsSensitive(options []string, sensitiveOptions []string) (bool, []string, []string, []string) {
|
||||||
|
// Because we have an FD opened on the subpath bind mount, the "bind" option
|
||||||
|
// needs to be included, otherwise the mount target will error as busy if you
|
||||||
|
// remount as readonly.
|
||||||
|
//
|
||||||
|
// As a consequence, all read only bind mounts will no longer change the underlying
|
||||||
|
// volume mount to be read only.
|
||||||
|
bindRemountOpts := []string{"bind", "remount"}
|
||||||
|
bindRemountSensitiveOpts := []string{}
|
||||||
|
bind := false
|
||||||
|
bindOpts := []string{"bind"}
|
||||||
|
|
||||||
|
// _netdev is a userspace mount option and does not automatically get added when
|
||||||
|
// bind mount is created and hence we must carry it over.
|
||||||
|
if checkForNetDev(options, sensitiveOptions) {
|
||||||
|
bindOpts = append(bindOpts, "_netdev")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
switch option {
|
||||||
|
case "bind":
|
||||||
|
bind = true
|
||||||
|
case "remount":
|
||||||
|
default:
|
||||||
|
bindRemountOpts = append(bindRemountOpts, option)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sensitiveOption := range sensitiveOptions {
|
||||||
|
switch sensitiveOption {
|
||||||
|
case "bind":
|
||||||
|
bind = true
|
||||||
|
case "remount":
|
||||||
|
default:
|
||||||
|
bindRemountSensitiveOpts = append(bindRemountSensitiveOpts, sensitiveOption)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bind, bindOpts, bindRemountOpts, bindRemountSensitiveOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkForNetDev(options []string, sensitiveOptions []string) bool {
|
||||||
|
for _, option := range options {
|
||||||
|
if option == "_netdev" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, sensitiveOption := range sensitiveOptions {
|
||||||
|
if sensitiveOption == "_netdev" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathWithinBase checks if give path is within given base directory.
|
||||||
|
func PathWithinBase(fullPath, basePath string) bool {
|
||||||
|
rel, err := filepath.Rel(basePath, fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if StartsWithBackstep(rel) {
|
||||||
|
// Needed to escape the base path.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartsWithBackstep checks if the given path starts with a backstep segment.
|
||||||
|
func StartsWithBackstep(rel string) bool {
|
||||||
|
// normalize to / and check for ../
|
||||||
|
return rel == ".." || strings.HasPrefix(filepath.ToSlash(rel), "../")
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanitizedOptionsForLogging will return a comma separated string containing
|
||||||
|
// options and sensitiveOptions. Each entry in sensitiveOptions will be
|
||||||
|
// replaced with the string sensitiveOptionsRemoved
|
||||||
|
// e.g. o1,o2,<masked>,<masked>
|
||||||
|
func sanitizedOptionsForLogging(options []string, sensitiveOptions []string) string {
|
||||||
|
separator := ""
|
||||||
|
if len(options) > 0 && len(sensitiveOptions) > 0 {
|
||||||
|
separator = ","
|
||||||
|
}
|
||||||
|
|
||||||
|
sensitiveOptionsStart := ""
|
||||||
|
sensitiveOptionsEnd := ""
|
||||||
|
if len(sensitiveOptions) > 0 {
|
||||||
|
sensitiveOptionsStart = strings.Repeat(sensitiveOptionsRemoved+",", len(sensitiveOptions)-1)
|
||||||
|
sensitiveOptionsEnd = sensitiveOptionsRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(options, ",") +
|
||||||
|
separator +
|
||||||
|
sensitiveOptionsStart +
|
||||||
|
sensitiveOptionsEnd
|
||||||
|
}
|
||||||
103
vendor/k8s.io/mount-utils/mount_helper_common.go
generated
vendored
Normal file
103
vendor/k8s.io/mount-utils/mount_helper_common.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CleanupMountPoint unmounts the given path and deletes the remaining directory
|
||||||
|
// if successful. If extensiveMountPointCheck is true IsNotMountPoint will be
|
||||||
|
// called instead of IsLikelyNotMountPoint. IsNotMountPoint is more expensive
|
||||||
|
// but properly handles bind mounts within the same fs.
|
||||||
|
func CleanupMountPoint(mountPath string, mounter Interface, extensiveMountPointCheck bool) error {
|
||||||
|
pathExists, pathErr := PathExists(mountPath)
|
||||||
|
if !pathExists {
|
||||||
|
klog.Warningf("Warning: Unmount skipped because path does not exist: %v", mountPath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
corruptedMnt := IsCorruptedMnt(pathErr)
|
||||||
|
if pathErr != nil && !corruptedMnt {
|
||||||
|
return fmt.Errorf("Error checking path: %v", pathErr)
|
||||||
|
}
|
||||||
|
return doCleanupMountPoint(mountPath, mounter, extensiveMountPointCheck, corruptedMnt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// doCleanupMountPoint unmounts the given path and
|
||||||
|
// deletes the remaining directory if successful.
|
||||||
|
// if extensiveMountPointCheck is true
|
||||||
|
// IsNotMountPoint will be called instead of IsLikelyNotMountPoint.
|
||||||
|
// IsNotMountPoint is more expensive but properly handles bind mounts within the same fs.
|
||||||
|
// if corruptedMnt is true, it means that the mountPath is a corrupted mountpoint, and the mount point check
|
||||||
|
// will be skipped
|
||||||
|
func doCleanupMountPoint(mountPath string, mounter Interface, extensiveMountPointCheck bool, corruptedMnt bool) error {
|
||||||
|
var notMnt bool
|
||||||
|
var err error
|
||||||
|
if !corruptedMnt {
|
||||||
|
if extensiveMountPointCheck {
|
||||||
|
notMnt, err = IsNotMountPoint(mounter, mountPath)
|
||||||
|
} else {
|
||||||
|
notMnt, err = mounter.IsLikelyNotMountPoint(mountPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if notMnt {
|
||||||
|
klog.Warningf("Warning: %q is not a mountpoint, deleting", mountPath)
|
||||||
|
return os.Remove(mountPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmount the mount path
|
||||||
|
klog.V(4).Infof("%q is a mountpoint, unmounting", mountPath)
|
||||||
|
if err := mounter.Unmount(mountPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if extensiveMountPointCheck {
|
||||||
|
notMnt, err = IsNotMountPoint(mounter, mountPath)
|
||||||
|
} else {
|
||||||
|
notMnt, err = mounter.IsLikelyNotMountPoint(mountPath)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if notMnt {
|
||||||
|
klog.V(4).Infof("%q is unmounted, deleting the directory", mountPath)
|
||||||
|
return os.Remove(mountPath)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Failed to unmount path %v", mountPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathExists returns true if the specified path exists.
|
||||||
|
// TODO: clean this up to use pkg/util/file/FileExists
|
||||||
|
func PathExists(path string) (bool, error) {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
return true, nil
|
||||||
|
} else if os.IsNotExist(err) {
|
||||||
|
return false, nil
|
||||||
|
} else if IsCorruptedMnt(err) {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
158
vendor/k8s.io/mount-utils/mount_helper_unix.go
generated
vendored
Normal file
158
vendor/k8s.io/mount-utils/mount_helper_unix.go
generated
vendored
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2019 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
utilio "k8s.io/utils/io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// At least number of fields per line in /proc/<pid>/mountinfo.
|
||||||
|
expectedAtLeastNumFieldsPerMountInfo = 10
|
||||||
|
// How many times to retry for a consistent read of /proc/mounts.
|
||||||
|
maxListTries = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsCorruptedMnt return true if err is about corrupted mount point
|
||||||
|
func IsCorruptedMnt(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var underlyingError error
|
||||||
|
switch pe := err.(type) {
|
||||||
|
case nil:
|
||||||
|
return false
|
||||||
|
case *os.PathError:
|
||||||
|
underlyingError = pe.Err
|
||||||
|
case *os.LinkError:
|
||||||
|
underlyingError = pe.Err
|
||||||
|
case *os.SyscallError:
|
||||||
|
underlyingError = pe.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
return underlyingError == syscall.ENOTCONN || underlyingError == syscall.ESTALE || underlyingError == syscall.EIO || underlyingError == syscall.EACCES
|
||||||
|
}
|
||||||
|
|
||||||
|
// MountInfo represents a single line in /proc/<pid>/mountinfo.
|
||||||
|
type MountInfo struct { // nolint: golint
|
||||||
|
// Unique ID for the mount (maybe reused after umount).
|
||||||
|
ID int
|
||||||
|
// The ID of the parent mount (or of self for the root of this mount namespace's mount tree).
|
||||||
|
ParentID int
|
||||||
|
// Major indicates one half of the device ID which identifies the device class
|
||||||
|
// (parsed from `st_dev` for files on this filesystem).
|
||||||
|
Major int
|
||||||
|
// Minor indicates one half of the device ID which identifies a specific
|
||||||
|
// instance of device (parsed from `st_dev` for files on this filesystem).
|
||||||
|
Minor int
|
||||||
|
// The pathname of the directory in the filesystem which forms the root of this mount.
|
||||||
|
Root string
|
||||||
|
// Mount source, filesystem-specific information. e.g. device, tmpfs name.
|
||||||
|
Source string
|
||||||
|
// Mount point, the pathname of the mount point.
|
||||||
|
MountPoint string
|
||||||
|
// Optional fieds, zero or more fields of the form "tag[:value]".
|
||||||
|
OptionalFields []string
|
||||||
|
// The filesystem type in the form "type[.subtype]".
|
||||||
|
FsType string
|
||||||
|
// Per-mount options.
|
||||||
|
MountOptions []string
|
||||||
|
// Per-superblock options.
|
||||||
|
SuperOptions []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseMountInfo parses /proc/xxx/mountinfo.
|
||||||
|
func ParseMountInfo(filename string) ([]MountInfo, error) {
|
||||||
|
content, err := utilio.ConsistentRead(filename, maxListTries)
|
||||||
|
if err != nil {
|
||||||
|
return []MountInfo{}, err
|
||||||
|
}
|
||||||
|
contentStr := string(content)
|
||||||
|
infos := []MountInfo{}
|
||||||
|
|
||||||
|
for _, line := range strings.Split(contentStr, "\n") {
|
||||||
|
if line == "" {
|
||||||
|
// the last split() item is empty string following the last \n
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// See `man proc` for authoritative description of format of the file.
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) < expectedAtLeastNumFieldsPerMountInfo {
|
||||||
|
return nil, fmt.Errorf("wrong number of fields in (expected at least %d, got %d): %s", expectedAtLeastNumFieldsPerMountInfo, len(fields), line)
|
||||||
|
}
|
||||||
|
id, err := strconv.Atoi(fields[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
parentID, err := strconv.Atoi(fields[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mm := strings.Split(fields[2], ":")
|
||||||
|
if len(mm) != 2 {
|
||||||
|
return nil, fmt.Errorf("parsing '%s' failed: unexpected minor:major pair %s", line, mm)
|
||||||
|
}
|
||||||
|
major, err := strconv.Atoi(mm[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing '%s' failed: unable to parse major device id, err:%v", mm[0], err)
|
||||||
|
}
|
||||||
|
minor, err := strconv.Atoi(mm[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing '%s' failed: unable to parse minor device id, err:%v", mm[1], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
info := MountInfo{
|
||||||
|
ID: id,
|
||||||
|
ParentID: parentID,
|
||||||
|
Major: major,
|
||||||
|
Minor: minor,
|
||||||
|
Root: fields[3],
|
||||||
|
MountPoint: fields[4],
|
||||||
|
MountOptions: strings.Split(fields[5], ","),
|
||||||
|
}
|
||||||
|
// All fields until "-" are "optional fields".
|
||||||
|
i := 6
|
||||||
|
for ; i < len(fields) && fields[i] != "-"; i++ {
|
||||||
|
info.OptionalFields = append(info.OptionalFields, fields[i])
|
||||||
|
}
|
||||||
|
// Parse the rest 3 fields.
|
||||||
|
i++
|
||||||
|
if len(fields)-i < 3 {
|
||||||
|
return nil, fmt.Errorf("expect 3 fields in %s, got %d", line, len(fields)-i)
|
||||||
|
}
|
||||||
|
info.FsType = fields[i]
|
||||||
|
info.Source = fields[i+1]
|
||||||
|
info.SuperOptions = strings.Split(fields[i+2], ",")
|
||||||
|
infos = append(infos, info)
|
||||||
|
}
|
||||||
|
return infos, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isMountPointMatch returns true if the path in mp is the same as dir.
|
||||||
|
// Handles case where mountpoint dir has been renamed due to stale NFS mount.
|
||||||
|
func isMountPointMatch(mp MountPoint, dir string) bool {
|
||||||
|
deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
|
||||||
|
return ((mp.Path == dir) || (mp.Path == deletedDir))
|
||||||
|
}
|
||||||
101
vendor/k8s.io/mount-utils/mount_helper_windows.go
generated
vendored
Normal file
101
vendor/k8s.io/mount-utils/mount_helper_windows.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2019 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// following failure codes are from https://docs.microsoft.com/en-us/windows/desktop/debug/system-error-codes--1300-1699-
|
||||||
|
// ERROR_BAD_NETPATH = 53
|
||||||
|
// ERROR_NETWORK_BUSY = 54
|
||||||
|
// ERROR_UNEXP_NET_ERR = 59
|
||||||
|
// ERROR_NETNAME_DELETED = 64
|
||||||
|
// ERROR_NETWORK_ACCESS_DENIED = 65
|
||||||
|
// ERROR_BAD_DEV_TYPE = 66
|
||||||
|
// ERROR_BAD_NET_NAME = 67
|
||||||
|
// ERROR_SESSION_CREDENTIAL_CONFLICT = 1219
|
||||||
|
// ERROR_LOGON_FAILURE = 1326
|
||||||
|
var errorNoList = [...]int{53, 54, 59, 64, 65, 66, 67, 1219, 1326}
|
||||||
|
|
||||||
|
// IsCorruptedMnt return true if err is about corrupted mount point
|
||||||
|
func IsCorruptedMnt(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var underlyingError error
|
||||||
|
switch pe := err.(type) {
|
||||||
|
case nil:
|
||||||
|
return false
|
||||||
|
case *os.PathError:
|
||||||
|
underlyingError = pe.Err
|
||||||
|
case *os.LinkError:
|
||||||
|
underlyingError = pe.Err
|
||||||
|
case *os.SyscallError:
|
||||||
|
underlyingError = pe.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ee, ok := underlyingError.(syscall.Errno); ok {
|
||||||
|
for _, errno := range errorNoList {
|
||||||
|
if int(ee) == errno {
|
||||||
|
klog.Warningf("IsCorruptedMnt failed with error: %v, error code: %v", err, errno)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormalizeWindowsPath makes sure the given path is a valid path on Windows
|
||||||
|
// systems by making sure all instances of `/` are replaced with `\\`, and the
|
||||||
|
// path beings with `c:`
|
||||||
|
func NormalizeWindowsPath(path string) string {
|
||||||
|
normalizedPath := strings.Replace(path, "/", "\\", -1)
|
||||||
|
if strings.HasPrefix(normalizedPath, "\\") {
|
||||||
|
normalizedPath = "c:" + normalizedPath
|
||||||
|
}
|
||||||
|
return normalizedPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateDiskNumber : disk number should be a number in [0, 99]
|
||||||
|
func ValidateDiskNumber(disk string) error {
|
||||||
|
diskNum, err := strconv.Atoi(disk)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("wrong disk number format: %q, err:%v", disk, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if diskNum < 0 || diskNum > 99 {
|
||||||
|
return fmt.Errorf("disk number out of range: %q", disk)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isMountPointMatch determines if the mountpoint matches the dir
|
||||||
|
func isMountPointMatch(mp MountPoint, dir string) bool {
|
||||||
|
return mp.Path == dir
|
||||||
|
}
|
||||||
575
vendor/k8s.io/mount-utils/mount_linux.go
generated
vendored
Normal file
575
vendor/k8s.io/mount-utils/mount_linux.go
generated
vendored
Normal file
@ -0,0 +1,575 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
utilexec "k8s.io/utils/exec"
|
||||||
|
utilio "k8s.io/utils/io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Number of fields per line in /proc/mounts as per the fstab man page.
|
||||||
|
expectedNumFieldsPerLine = 6
|
||||||
|
// Location of the mount file to use
|
||||||
|
procMountsPath = "/proc/mounts"
|
||||||
|
// Location of the mountinfo file
|
||||||
|
procMountInfoPath = "/proc/self/mountinfo"
|
||||||
|
// 'fsck' found errors and corrected them
|
||||||
|
fsckErrorsCorrected = 1
|
||||||
|
// 'fsck' found errors but exited without correcting them
|
||||||
|
fsckErrorsUncorrected = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mounter provides the default implementation of mount.Interface
|
||||||
|
// for the linux platform. This implementation assumes that the
|
||||||
|
// kubelet is running in the host's root mount namespace.
|
||||||
|
type Mounter struct {
|
||||||
|
mounterPath string
|
||||||
|
withSystemd bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a mount.Interface for the current system.
|
||||||
|
// It provides options to override the default mounter behavior.
|
||||||
|
// mounterPath allows using an alternative to `/bin/mount` for mounting.
|
||||||
|
func New(mounterPath string) Interface {
|
||||||
|
return &Mounter{
|
||||||
|
mounterPath: mounterPath,
|
||||||
|
withSystemd: detectSystemd(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount mounts source to target as fstype with given options. 'source' and 'fstype' must
|
||||||
|
// be an empty string in case it's not required, e.g. for remount, or for auto filesystem
|
||||||
|
// type, where kernel handles fstype for you. The mount 'options' is a list of options,
|
||||||
|
// currently come from mount(8), e.g. "ro", "remount", "bind", etc. If no more option is
|
||||||
|
// required, call Mount with an empty string list or nil.
|
||||||
|
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
|
||||||
|
return mounter.MountSensitive(source, target, fstype, options, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MountSensitive is the same as Mount() but this method allows
|
||||||
|
// sensitiveOptions to be passed in a separate parameter from the normal
|
||||||
|
// mount options and ensures the sensitiveOptions are never logged. This
|
||||||
|
// method should be used by callers that pass sensitive material (like
|
||||||
|
// passwords) as mount options.
|
||||||
|
func (mounter *Mounter) MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
||||||
|
// Path to mounter binary if containerized mounter is needed. Otherwise, it is set to empty.
|
||||||
|
// All Linux distros are expected to be shipped with a mount utility that a support bind mounts.
|
||||||
|
mounterPath := ""
|
||||||
|
bind, bindOpts, bindRemountOpts, bindRemountOptsSensitive := MakeBindOptsSensitive(options, sensitiveOptions)
|
||||||
|
if bind {
|
||||||
|
err := mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindOpts, bindRemountOptsSensitive, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindRemountOpts, bindRemountOptsSensitive, true)
|
||||||
|
}
|
||||||
|
// The list of filesystems that require containerized mounter on GCI image cluster
|
||||||
|
fsTypesNeedMounter := map[string]struct{}{
|
||||||
|
"nfs": {},
|
||||||
|
"glusterfs": {},
|
||||||
|
"ceph": {},
|
||||||
|
"cifs": {},
|
||||||
|
}
|
||||||
|
if _, ok := fsTypesNeedMounter[fstype]; ok {
|
||||||
|
mounterPath = mounter.mounterPath
|
||||||
|
}
|
||||||
|
return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, options, sensitiveOptions, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MountSensitiveWithoutSystemd is the same as MountSensitive() but disable using systemd mount.
|
||||||
|
func (mounter *Mounter) MountSensitiveWithoutSystemd(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
||||||
|
mounterPath := ""
|
||||||
|
bind, bindOpts, bindRemountOpts, bindRemountOptsSensitive := MakeBindOptsSensitive(options, sensitiveOptions)
|
||||||
|
if bind {
|
||||||
|
err := mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindOpts, bindRemountOptsSensitive, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindRemountOpts, bindRemountOptsSensitive, false)
|
||||||
|
}
|
||||||
|
// The list of filesystems that require containerized mounter on GCI image cluster
|
||||||
|
fsTypesNeedMounter := map[string]struct{}{
|
||||||
|
"nfs": {},
|
||||||
|
"glusterfs": {},
|
||||||
|
"ceph": {},
|
||||||
|
"cifs": {},
|
||||||
|
}
|
||||||
|
if _, ok := fsTypesNeedMounter[fstype]; ok {
|
||||||
|
mounterPath = mounter.mounterPath
|
||||||
|
}
|
||||||
|
return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, options, sensitiveOptions, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// doMount runs the mount command. mounterPath is the path to mounter binary if containerized mounter is used.
|
||||||
|
// sensitiveOptions is an extension of options except they will not be logged (because they may contain sensitive material)
|
||||||
|
// systemdMountRequired is an extension of option to decide whether uses systemd mount.
|
||||||
|
func (mounter *Mounter) doMount(mounterPath string, mountCmd string, source string, target string, fstype string, options []string, sensitiveOptions []string, systemdMountRequired bool) error {
|
||||||
|
mountArgs, mountArgsLogStr := MakeMountArgsSensitive(source, target, fstype, options, sensitiveOptions)
|
||||||
|
if len(mounterPath) > 0 {
|
||||||
|
mountArgs = append([]string{mountCmd}, mountArgs...)
|
||||||
|
mountArgsLogStr = mountCmd + " " + mountArgsLogStr
|
||||||
|
mountCmd = mounterPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if mounter.withSystemd && systemdMountRequired {
|
||||||
|
// Try to run mount via systemd-run --scope. This will escape the
|
||||||
|
// service where kubelet runs and any fuse daemons will be started in a
|
||||||
|
// specific scope. kubelet service than can be restarted without killing
|
||||||
|
// these fuse daemons.
|
||||||
|
//
|
||||||
|
// Complete command line (when mounterPath is not used):
|
||||||
|
// systemd-run --description=... --scope -- mount -t <type> <what> <where>
|
||||||
|
//
|
||||||
|
// Expected flow:
|
||||||
|
// * systemd-run creates a transient scope (=~ cgroup) and executes its
|
||||||
|
// argument (/bin/mount) there.
|
||||||
|
// * mount does its job, forks a fuse daemon if necessary and finishes.
|
||||||
|
// (systemd-run --scope finishes at this point, returning mount's exit
|
||||||
|
// code and stdout/stderr - thats one of --scope benefits).
|
||||||
|
// * systemd keeps the fuse daemon running in the scope (i.e. in its own
|
||||||
|
// cgroup) until the fuse daemon dies (another --scope benefit).
|
||||||
|
// Kubelet service can be restarted and the fuse daemon survives.
|
||||||
|
// * When the fuse daemon dies (e.g. during unmount) systemd removes the
|
||||||
|
// scope automatically.
|
||||||
|
//
|
||||||
|
// systemd-mount is not used because it's too new for older distros
|
||||||
|
// (CentOS 7, Debian Jessie).
|
||||||
|
mountCmd, mountArgs, mountArgsLogStr = AddSystemdScopeSensitive("systemd-run", target, mountCmd, mountArgs, mountArgsLogStr)
|
||||||
|
// } else {
|
||||||
|
// No systemd-run on the host (or we failed to check it), assume kubelet
|
||||||
|
// does not run as a systemd service.
|
||||||
|
// No code here, mountCmd and mountArgs are already populated.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logging with sensitive mount options removed.
|
||||||
|
klog.V(4).Infof("Mounting cmd (%s) with arguments (%s)", mountCmd, mountArgsLogStr)
|
||||||
|
command := exec.Command(mountCmd, mountArgs...)
|
||||||
|
output, err := command.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Mount failed: %v\nMounting command: %s\nMounting arguments: %s\nOutput: %s\n", err, mountCmd, mountArgsLogStr, string(output))
|
||||||
|
return fmt.Errorf("mount failed: %v\nMounting command: %s\nMounting arguments: %s\nOutput: %s",
|
||||||
|
err, mountCmd, mountArgsLogStr, string(output))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// detectSystemd returns true if OS runs with systemd as init. When not sure
|
||||||
|
// (permission errors, ...), it returns false.
|
||||||
|
// There may be different ways how to detect systemd, this one makes sure that
|
||||||
|
// systemd-runs (needed by Mount()) works.
|
||||||
|
func detectSystemd() bool {
|
||||||
|
if _, err := exec.LookPath("systemd-run"); err != nil {
|
||||||
|
klog.V(2).Infof("Detected OS without systemd")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Try to run systemd-run --scope /bin/true, that should be enough
|
||||||
|
// to make sure that systemd is really running and not just installed,
|
||||||
|
// which happens when running in a container with a systemd-based image
|
||||||
|
// but with different pid 1.
|
||||||
|
cmd := exec.Command("systemd-run", "--description=Kubernetes systemd probe", "--scope", "true")
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
klog.V(2).Infof("Cannot run systemd-run, assuming non-systemd OS")
|
||||||
|
klog.V(4).Infof("systemd-run output: %s, failed with: %v", string(output), err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
klog.V(2).Infof("Detected OS with systemd")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeMountArgs makes the arguments to the mount(8) command.
|
||||||
|
// options MUST not contain sensitive material (like passwords).
|
||||||
|
func MakeMountArgs(source, target, fstype string, options []string) (mountArgs []string) {
|
||||||
|
mountArgs, _ = MakeMountArgsSensitive(source, target, fstype, options, nil /* sensitiveOptions */)
|
||||||
|
return mountArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeMountArgsSensitive makes the arguments to the mount(8) command.
|
||||||
|
// sensitiveOptions is an extension of options except they will not be logged (because they may contain sensitive material)
|
||||||
|
func MakeMountArgsSensitive(source, target, fstype string, options []string, sensitiveOptions []string) (mountArgs []string, mountArgsLogStr string) {
|
||||||
|
// Build mount command as follows:
|
||||||
|
// mount [-t $fstype] [-o $options] [$source] $target
|
||||||
|
mountArgs = []string{}
|
||||||
|
mountArgsLogStr = ""
|
||||||
|
if len(fstype) > 0 {
|
||||||
|
mountArgs = append(mountArgs, "-t", fstype)
|
||||||
|
mountArgsLogStr += strings.Join(mountArgs, " ")
|
||||||
|
}
|
||||||
|
if len(options) > 0 || len(sensitiveOptions) > 0 {
|
||||||
|
combinedOptions := []string{}
|
||||||
|
combinedOptions = append(combinedOptions, options...)
|
||||||
|
combinedOptions = append(combinedOptions, sensitiveOptions...)
|
||||||
|
mountArgs = append(mountArgs, "-o", strings.Join(combinedOptions, ","))
|
||||||
|
// exclude sensitiveOptions from log string
|
||||||
|
mountArgsLogStr += " -o " + sanitizedOptionsForLogging(options, sensitiveOptions)
|
||||||
|
}
|
||||||
|
if len(source) > 0 {
|
||||||
|
mountArgs = append(mountArgs, source)
|
||||||
|
mountArgsLogStr += " " + source
|
||||||
|
}
|
||||||
|
mountArgs = append(mountArgs, target)
|
||||||
|
mountArgsLogStr += " " + target
|
||||||
|
|
||||||
|
return mountArgs, mountArgsLogStr
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSystemdScope adds "system-run --scope" to given command line
|
||||||
|
// If args contains sensitive material, use AddSystemdScopeSensitive to construct
|
||||||
|
// a safe to log string.
|
||||||
|
func AddSystemdScope(systemdRunPath, mountName, command string, args []string) (string, []string) {
|
||||||
|
descriptionArg := fmt.Sprintf("--description=Kubernetes transient mount for %s", mountName)
|
||||||
|
systemdRunArgs := []string{descriptionArg, "--scope", "--", command}
|
||||||
|
return systemdRunPath, append(systemdRunArgs, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSystemdScopeSensitive adds "system-run --scope" to given command line
|
||||||
|
// It also accepts takes a sanitized string containing mount arguments, mountArgsLogStr,
|
||||||
|
// and returns the string appended to the systemd command for logging.
|
||||||
|
func AddSystemdScopeSensitive(systemdRunPath, mountName, command string, args []string, mountArgsLogStr string) (string, []string, string) {
|
||||||
|
descriptionArg := fmt.Sprintf("--description=Kubernetes transient mount for %s", mountName)
|
||||||
|
systemdRunArgs := []string{descriptionArg, "--scope", "--", command}
|
||||||
|
return systemdRunPath, append(systemdRunArgs, args...), strings.Join(systemdRunArgs, " ") + " " + mountArgsLogStr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmount unmounts the target.
|
||||||
|
func (mounter *Mounter) Unmount(target string) error {
|
||||||
|
klog.V(4).Infof("Unmounting %s", target)
|
||||||
|
command := exec.Command("umount", target)
|
||||||
|
output, err := command.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unmount failed: %v\nUnmounting arguments: %s\nOutput: %s", err, target, string(output))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns a list of all mounted filesystems.
|
||||||
|
func (*Mounter) List() ([]MountPoint, error) {
|
||||||
|
return ListProcMounts(procMountsPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLikelyNotMountPoint determines if a directory is not a mountpoint.
|
||||||
|
// It is fast but not necessarily ALWAYS correct. If the path is in fact
|
||||||
|
// a bind mount from one part of a mount to another it will not be detected.
|
||||||
|
// It also can not distinguish between mountpoints and symbolic links.
|
||||||
|
// mkdir /tmp/a /tmp/b; mount --bind /tmp/a /tmp/b; IsLikelyNotMountPoint("/tmp/b")
|
||||||
|
// will return true. When in fact /tmp/b is a mount point. If this situation
|
||||||
|
// is of interest to you, don't use this function...
|
||||||
|
func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||||
|
stat, err := os.Stat(file)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
rootStat, err := os.Stat(filepath.Dir(strings.TrimSuffix(file, "/")))
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
// If the directory has a different device as parent, then it is a mountpoint.
|
||||||
|
if stat.Sys().(*syscall.Stat_t).Dev != rootStat.Sys().(*syscall.Stat_t).Dev {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMountRefs finds all mount references to pathname, returns a
|
||||||
|
// list of paths. Path could be a mountpoint or a normal
|
||||||
|
// directory (for bind mount).
|
||||||
|
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
|
||||||
|
pathExists, pathErr := PathExists(pathname)
|
||||||
|
if !pathExists {
|
||||||
|
return []string{}, nil
|
||||||
|
} else if IsCorruptedMnt(pathErr) {
|
||||||
|
klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", pathname)
|
||||||
|
return []string{}, nil
|
||||||
|
} else if pathErr != nil {
|
||||||
|
return nil, fmt.Errorf("error checking path %s: %v", pathname, pathErr)
|
||||||
|
}
|
||||||
|
realpath, err := filepath.EvalSymlinks(pathname)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return SearchMountPoints(realpath, procMountInfoPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkAndRepairFileSystem checks and repairs filesystems using command fsck.
|
||||||
|
func (mounter *SafeFormatAndMount) checkAndRepairFilesystem(source string) error {
|
||||||
|
klog.V(4).Infof("Checking for issues with fsck on disk: %s", source)
|
||||||
|
args := []string{"-a", source}
|
||||||
|
out, err := mounter.Exec.Command("fsck", args...).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
ee, isExitError := err.(utilexec.ExitError)
|
||||||
|
switch {
|
||||||
|
case err == utilexec.ErrExecutableNotFound:
|
||||||
|
klog.Warningf("'fsck' not found on system; continuing mount without running 'fsck'.")
|
||||||
|
case isExitError && ee.ExitStatus() == fsckErrorsCorrected:
|
||||||
|
klog.Infof("Device %s has errors which were corrected by fsck.", source)
|
||||||
|
case isExitError && ee.ExitStatus() == fsckErrorsUncorrected:
|
||||||
|
return NewMountError(HasFilesystemErrors, "'fsck' found errors on device %s but could not correct them: %s", source, string(out))
|
||||||
|
case isExitError && ee.ExitStatus() > fsckErrorsUncorrected:
|
||||||
|
klog.Infof("`fsck` error %s", string(out))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatAndMount uses unix utils to format and mount the given disk
|
||||||
|
func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
||||||
|
readOnly := false
|
||||||
|
for _, option := range options {
|
||||||
|
if option == "ro" {
|
||||||
|
readOnly = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !readOnly {
|
||||||
|
// Check sensitiveOptions for ro
|
||||||
|
for _, option := range sensitiveOptions {
|
||||||
|
if option == "ro" {
|
||||||
|
readOnly = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
options = append(options, "defaults")
|
||||||
|
mountErrorValue := UnknownMountError
|
||||||
|
|
||||||
|
// Check if the disk is already formatted
|
||||||
|
existingFormat, err := mounter.GetDiskFormat(source)
|
||||||
|
if err != nil {
|
||||||
|
return NewMountError(GetDiskFormatFailed, "failed to get disk format of disk %s: %v", source, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use 'ext4' as the default
|
||||||
|
if len(fstype) == 0 {
|
||||||
|
fstype = "ext4"
|
||||||
|
}
|
||||||
|
|
||||||
|
if existingFormat == "" {
|
||||||
|
// Do not attempt to format the disk if mounting as readonly, return an error to reflect this.
|
||||||
|
if readOnly {
|
||||||
|
return NewMountError(UnformattedReadOnly, "cannot mount unformatted disk %s as we are manipulating it in read-only mode", source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disk is unformatted so format it.
|
||||||
|
args := []string{source}
|
||||||
|
if fstype == "ext4" || fstype == "ext3" {
|
||||||
|
args = []string{
|
||||||
|
"-F", // Force flag
|
||||||
|
"-m0", // Zero blocks reserved for super-user
|
||||||
|
source,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.Infof("Disk %q appears to be unformatted, attempting to format as type: %q with options: %v", source, fstype, args)
|
||||||
|
output, err := mounter.Exec.Command("mkfs."+fstype, args...).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
// Do not log sensitiveOptions only options
|
||||||
|
sensitiveOptionsLog := sanitizedOptionsForLogging(options, sensitiveOptions)
|
||||||
|
detailedErr := fmt.Sprintf("format of disk %q failed: type:(%q) target:(%q) options:(%q) errcode:(%v) output:(%v) ", source, fstype, target, sensitiveOptionsLog, err, string(output))
|
||||||
|
klog.Error(detailedErr)
|
||||||
|
return NewMountError(FormatFailed, detailedErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.Infof("Disk successfully formatted (mkfs): %s - %s %s", fstype, source, target)
|
||||||
|
} else {
|
||||||
|
if fstype != existingFormat {
|
||||||
|
// Verify that the disk is formatted with filesystem type we are expecting
|
||||||
|
mountErrorValue = FilesystemMismatch
|
||||||
|
klog.Warningf("Configured to mount disk %s as %s but current format is %s, things might break", source, existingFormat, fstype)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !readOnly {
|
||||||
|
// Run check tools on the disk to fix repairable issues, only do this for formatted volumes requested as rw.
|
||||||
|
err := mounter.checkAndRepairFilesystem(source)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount the disk
|
||||||
|
klog.V(4).Infof("Attempting to mount disk %s in %s format at %s", source, fstype, target)
|
||||||
|
if err := mounter.MountSensitive(source, target, fstype, options, sensitiveOptions); err != nil {
|
||||||
|
return NewMountError(mountErrorValue, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDiskFormat uses 'blkid' to see if the given disk is unformatted
|
||||||
|
func (mounter *SafeFormatAndMount) GetDiskFormat(disk string) (string, error) {
|
||||||
|
args := []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", disk}
|
||||||
|
klog.V(4).Infof("Attempting to determine if disk %q is formatted using blkid with args: (%v)", disk, args)
|
||||||
|
dataOut, err := mounter.Exec.Command("blkid", args...).CombinedOutput()
|
||||||
|
output := string(dataOut)
|
||||||
|
klog.V(4).Infof("Output: %q", output)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if exit, ok := err.(utilexec.ExitError); ok {
|
||||||
|
if exit.ExitStatus() == 2 {
|
||||||
|
// Disk device is unformatted.
|
||||||
|
// For `blkid`, if the specified token (TYPE/PTTYPE, etc) was
|
||||||
|
// not found, or no (specified) devices could be identified, an
|
||||||
|
// exit code of 2 is returned.
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
klog.Errorf("Could not determine if disk %q is formatted (%v)", disk, err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var fstype, pttype string
|
||||||
|
|
||||||
|
lines := strings.Split(output, "\n")
|
||||||
|
for _, l := range lines {
|
||||||
|
if len(l) <= 0 {
|
||||||
|
// Ignore empty line.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cs := strings.Split(l, "=")
|
||||||
|
if len(cs) != 2 {
|
||||||
|
return "", fmt.Errorf("blkid returns invalid output: %s", output)
|
||||||
|
}
|
||||||
|
// TYPE is filesystem type, and PTTYPE is partition table type, according
|
||||||
|
// to https://www.kernel.org/pub/linux/utils/util-linux/v2.21/libblkid-docs/.
|
||||||
|
if cs[0] == "TYPE" {
|
||||||
|
fstype = cs[1]
|
||||||
|
} else if cs[0] == "PTTYPE" {
|
||||||
|
pttype = cs[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pttype) > 0 {
|
||||||
|
klog.V(4).Infof("Disk %s detected partition table type: %s", disk, pttype)
|
||||||
|
// Returns a special non-empty string as filesystem type, then kubelet
|
||||||
|
// will not format it.
|
||||||
|
return "unknown data, probably partitions", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fstype, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListProcMounts is shared with NsEnterMounter
|
||||||
|
func ListProcMounts(mountFilePath string) ([]MountPoint, error) {
|
||||||
|
content, err := utilio.ConsistentRead(mountFilePath, maxListTries)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return parseProcMounts(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseProcMounts(content []byte) ([]MountPoint, error) {
|
||||||
|
out := []MountPoint{}
|
||||||
|
lines := strings.Split(string(content), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if line == "" {
|
||||||
|
// the last split() item is empty string following the last \n
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) != expectedNumFieldsPerLine {
|
||||||
|
// Do not log line in case it contains sensitive Mount options
|
||||||
|
return nil, fmt.Errorf("wrong number of fields (expected %d, got %d)", expectedNumFieldsPerLine, len(fields))
|
||||||
|
}
|
||||||
|
|
||||||
|
mp := MountPoint{
|
||||||
|
Device: fields[0],
|
||||||
|
Path: fields[1],
|
||||||
|
Type: fields[2],
|
||||||
|
Opts: strings.Split(fields[3], ","),
|
||||||
|
}
|
||||||
|
|
||||||
|
freq, err := strconv.Atoi(fields[4])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mp.Freq = freq
|
||||||
|
|
||||||
|
pass, err := strconv.Atoi(fields[5])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mp.Pass = pass
|
||||||
|
|
||||||
|
out = append(out, mp)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchMountPoints finds all mount references to the source, returns a list of
|
||||||
|
// mountpoints.
|
||||||
|
// The source can be a mount point or a normal directory (bind mount). We
|
||||||
|
// didn't support device because there is no use case by now.
|
||||||
|
// Some filesystems may share a source name, e.g. tmpfs. And for bind mounting,
|
||||||
|
// it's possible to mount a non-root path of a filesystem, so we need to use
|
||||||
|
// root path and major:minor to represent mount source uniquely.
|
||||||
|
// This implementation is shared between Linux and NsEnterMounter
|
||||||
|
func SearchMountPoints(hostSource, mountInfoPath string) ([]string, error) {
|
||||||
|
mis, err := ParseMountInfo(mountInfoPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mountID := 0
|
||||||
|
rootPath := ""
|
||||||
|
major := -1
|
||||||
|
minor := -1
|
||||||
|
|
||||||
|
// Finding the underlying root path and major:minor if possible.
|
||||||
|
// We need search in backward order because it's possible for later mounts
|
||||||
|
// to overlap earlier mounts.
|
||||||
|
for i := len(mis) - 1; i >= 0; i-- {
|
||||||
|
if hostSource == mis[i].MountPoint || PathWithinBase(hostSource, mis[i].MountPoint) {
|
||||||
|
// If it's a mount point or path under a mount point.
|
||||||
|
mountID = mis[i].ID
|
||||||
|
rootPath = filepath.Join(mis[i].Root, strings.TrimPrefix(hostSource, mis[i].MountPoint))
|
||||||
|
major = mis[i].Major
|
||||||
|
minor = mis[i].Minor
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rootPath == "" || major == -1 || minor == -1 {
|
||||||
|
return nil, fmt.Errorf("failed to get root path and major:minor for %s", hostSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
var refs []string
|
||||||
|
for i := range mis {
|
||||||
|
if mis[i].ID == mountID {
|
||||||
|
// Ignore mount entry for mount source itself.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if mis[i].Root == rootPath && mis[i].Major == major && mis[i].Minor == minor {
|
||||||
|
refs = append(refs, mis[i].MountPoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return refs, nil
|
||||||
|
}
|
||||||
82
vendor/k8s.io/mount-utils/mount_unsupported.go
generated
vendored
Normal file
82
vendor/k8s.io/mount-utils/mount_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// +build !linux,!windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mounter implements mount.Interface for unsupported platforms
|
||||||
|
type Mounter struct {
|
||||||
|
mounterPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
var errUnsupported = errors.New("util/mount on this platform is not supported")
|
||||||
|
|
||||||
|
// New returns a mount.Interface for the current system.
|
||||||
|
// It provides options to override the default mounter behavior.
|
||||||
|
// mounterPath allows using an alternative to `/bin/mount` for mounting.
|
||||||
|
func New(mounterPath string) Interface {
|
||||||
|
return &Mounter{
|
||||||
|
mounterPath: mounterPath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount always returns an error on unsupported platforms
|
||||||
|
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
|
||||||
|
return errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// MountSensitive always returns an error on unsupported platforms
|
||||||
|
func (mounter *Mounter) MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
||||||
|
return errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// MountSensitiveWithoutSystemd always returns an error on unsupported platforms
|
||||||
|
func (mounter *Mounter) MountSensitiveWithoutSystemd(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
||||||
|
return errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmount always returns an error on unsupported platforms
|
||||||
|
func (mounter *Mounter) Unmount(target string) error {
|
||||||
|
return errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// List always returns an error on unsupported platforms
|
||||||
|
func (mounter *Mounter) List() ([]MountPoint, error) {
|
||||||
|
return []MountPoint{}, errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLikelyNotMountPoint always returns an error on unsupported platforms
|
||||||
|
func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||||
|
return true, errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMountRefs always returns an error on unsupported platforms
|
||||||
|
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
|
||||||
|
return nil, errUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
||||||
|
return mounter.Interface.Mount(source, target, fstype, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) {
|
||||||
|
return true, errUnsupported
|
||||||
|
}
|
||||||
330
vendor/k8s.io/mount-utils/mount_windows.go
generated
vendored
Normal file
330
vendor/k8s.io/mount-utils/mount_windows.go
generated
vendored
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
"k8s.io/utils/keymutex"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
accessDenied string = "access is denied"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mounter provides the default implementation of mount.Interface
|
||||||
|
// for the windows platform. This implementation assumes that the
|
||||||
|
// kubelet is running in the host's root mount namespace.
|
||||||
|
type Mounter struct {
|
||||||
|
mounterPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a mount.Interface for the current system.
|
||||||
|
// It provides options to override the default mounter behavior.
|
||||||
|
// mounterPath allows using an alternative to `/bin/mount` for mounting.
|
||||||
|
func New(mounterPath string) Interface {
|
||||||
|
return &Mounter{
|
||||||
|
mounterPath: mounterPath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// acquire lock for smb mount
|
||||||
|
var getSMBMountMutex = keymutex.NewHashed(0)
|
||||||
|
|
||||||
|
// Mount : mounts source to target with given options.
|
||||||
|
// currently only supports cifs(smb), bind mount(for disk)
|
||||||
|
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
|
||||||
|
return mounter.MountSensitive(source, target, fstype, options, nil /* sensitiveOptions */)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MountSensitiveWithoutSystemd is the same as MountSensitive() but disable using ssytemd mount.
|
||||||
|
// Windows not supported systemd mount, this function degrades to MountSensitive().
|
||||||
|
func (mounter *Mounter) MountSensitiveWithoutSystemd(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
||||||
|
return mounter.MountSensitive(source, target, fstype, options, sensitiveOptions /* sensitiveOptions */)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MountSensitive is the same as Mount() but this method allows
|
||||||
|
// sensitiveOptions to be passed in a separate parameter from the normal
|
||||||
|
// mount options and ensures the sensitiveOptions are never logged. This
|
||||||
|
// method should be used by callers that pass sensitive material (like
|
||||||
|
// passwords) as mount options.
|
||||||
|
func (mounter *Mounter) MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
||||||
|
target = NormalizeWindowsPath(target)
|
||||||
|
sanitizedOptionsForLogging := sanitizedOptionsForLogging(options, sensitiveOptions)
|
||||||
|
|
||||||
|
if source == "tmpfs" {
|
||||||
|
klog.V(3).Infof("mounting source (%q), target (%q), with options (%q)", source, target, sanitizedOptionsForLogging)
|
||||||
|
return os.MkdirAll(target, 0755)
|
||||||
|
}
|
||||||
|
|
||||||
|
parentDir := filepath.Dir(target)
|
||||||
|
if err := os.MkdirAll(parentDir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.V(4).Infof("mount options(%q) source:%q, target:%q, fstype:%q, begin to mount",
|
||||||
|
sanitizedOptionsForLogging, source, target, fstype)
|
||||||
|
bindSource := source
|
||||||
|
|
||||||
|
if bind, _, _, _ := MakeBindOptsSensitive(options, sensitiveOptions); bind {
|
||||||
|
bindSource = NormalizeWindowsPath(source)
|
||||||
|
} else {
|
||||||
|
allOptions := []string{}
|
||||||
|
allOptions = append(allOptions, options...)
|
||||||
|
allOptions = append(allOptions, sensitiveOptions...)
|
||||||
|
if len(allOptions) < 2 {
|
||||||
|
return fmt.Errorf("mount options(%q) should have at least 2 options, current number:%d, source:%q, target:%q",
|
||||||
|
sanitizedOptionsForLogging, len(allOptions), source, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// currently only cifs mount is supported
|
||||||
|
if strings.ToLower(fstype) != "cifs" {
|
||||||
|
return fmt.Errorf("only cifs mount is supported now, fstype: %q, mounting source (%q), target (%q), with options (%q)", fstype, source, target, sanitizedOptionsForLogging)
|
||||||
|
}
|
||||||
|
|
||||||
|
// lock smb mount for the same source
|
||||||
|
getSMBMountMutex.LockKey(source)
|
||||||
|
defer getSMBMountMutex.UnlockKey(source)
|
||||||
|
|
||||||
|
username := allOptions[0]
|
||||||
|
password := allOptions[1]
|
||||||
|
if output, err := newSMBMapping(username, password, source); err != nil {
|
||||||
|
klog.Warningf("SMB Mapping(%s) returned with error(%v), output(%s)", source, err, string(output))
|
||||||
|
if isSMBMappingExist(source) {
|
||||||
|
valid, err := isValidPath(source)
|
||||||
|
if !valid {
|
||||||
|
if err == nil || isAccessDeniedError(err) {
|
||||||
|
klog.V(2).Infof("SMB Mapping(%s) already exists while it's not valid, return error: %v, now begin to remove and remount", source, err)
|
||||||
|
if output, err = removeSMBMapping(source); err != nil {
|
||||||
|
return fmt.Errorf("Remove-SmbGlobalMapping failed: %v, output: %q", err, output)
|
||||||
|
}
|
||||||
|
if output, err := newSMBMapping(username, password, source); err != nil {
|
||||||
|
return fmt.Errorf("New-SmbGlobalMapping(%s) failed: %v, output: %q", source, err, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
klog.V(2).Infof("SMB Mapping(%s) already exists and is still valid, skip error(%v)", source, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("New-SmbGlobalMapping(%s) failed: %v, output: %q", source, err, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is an issue in golang where EvalSymlinks fails on Windows when passed a
|
||||||
|
// UNC share root path without a trailing backslash.
|
||||||
|
// Ex: \\SERVER\share will fail to resolve but \\SERVER\share\ will resolve
|
||||||
|
// containerD on Windows calls EvalSymlinks so we'll add the backslash when making the symlink if it is missing.
|
||||||
|
// https://github.com/golang/go/pull/42096 fixes this issue in golang but a fix will not be available until
|
||||||
|
// golang v1.16
|
||||||
|
mklinkSource := bindSource
|
||||||
|
if !strings.HasSuffix(mklinkSource, "\\") {
|
||||||
|
mklinkSource = mklinkSource + "\\"
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := exec.Command("cmd", "/c", "mklink", "/D", target, mklinkSource).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("mklink failed: %v, source(%q) target(%q) output: %q", err, mklinkSource, target, string(output))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
klog.V(2).Infof("mklink source(%q) on target(%q) successfully, output: %q", mklinkSource, target, string(output))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// do the SMB mount with username, password, remotepath
|
||||||
|
// return (output, error)
|
||||||
|
func newSMBMapping(username, password, remotepath string) (string, error) {
|
||||||
|
if username == "" || password == "" || remotepath == "" {
|
||||||
|
return "", fmt.Errorf("invalid parameter(username: %s, password: %s, remoteapth: %s)", username, sensitiveOptionsRemoved, remotepath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// use PowerShell Environment Variables to store user input string to prevent command line injection
|
||||||
|
// https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-5.1
|
||||||
|
cmdLine := `$PWord = ConvertTo-SecureString -String $Env:smbpassword -AsPlainText -Force` +
|
||||||
|
`;$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Env:smbuser, $PWord` +
|
||||||
|
`;New-SmbGlobalMapping -RemotePath $Env:smbremotepath -Credential $Credential`
|
||||||
|
cmd := exec.Command("powershell", "/c", cmdLine)
|
||||||
|
cmd.Env = append(os.Environ(),
|
||||||
|
fmt.Sprintf("smbuser=%s", username),
|
||||||
|
fmt.Sprintf("smbpassword=%s", password),
|
||||||
|
fmt.Sprintf("smbremotepath=%s", remotepath))
|
||||||
|
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
return string(output), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether remotepath is already mounted
|
||||||
|
func isSMBMappingExist(remotepath string) bool {
|
||||||
|
cmd := exec.Command("powershell", "/c", `Get-SmbGlobalMapping -RemotePath $Env:smbremotepath`)
|
||||||
|
cmd.Env = append(os.Environ(), fmt.Sprintf("smbremotepath=%s", remotepath))
|
||||||
|
_, err := cmd.CombinedOutput()
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether remotepath is valid
|
||||||
|
// return (true, nil) if remotepath is valid
|
||||||
|
func isValidPath(remotepath string) (bool, error) {
|
||||||
|
cmd := exec.Command("powershell", "/c", `Test-Path $Env:remoteapth`)
|
||||||
|
cmd.Env = append(os.Environ(), fmt.Sprintf("remoteapth=%s", remotepath))
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("returned output: %s, error: %v", string(output), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.HasPrefix(strings.ToLower(string(output)), "true"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAccessDeniedError(err error) bool {
|
||||||
|
return err != nil && strings.Contains(strings.ToLower(err.Error()), accessDenied)
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove SMB mapping
|
||||||
|
func removeSMBMapping(remotepath string) (string, error) {
|
||||||
|
cmd := exec.Command("powershell", "/c", `Remove-SmbGlobalMapping -RemotePath $Env:smbremotepath -Force`)
|
||||||
|
cmd.Env = append(os.Environ(), fmt.Sprintf("smbremotepath=%s", remotepath))
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
return string(output), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmount unmounts the target.
|
||||||
|
func (mounter *Mounter) Unmount(target string) error {
|
||||||
|
klog.V(4).Infof("azureMount: Unmount target (%q)", target)
|
||||||
|
target = NormalizeWindowsPath(target)
|
||||||
|
if output, err := exec.Command("cmd", "/c", "rmdir", target).CombinedOutput(); err != nil {
|
||||||
|
klog.Errorf("rmdir failed: %v, output: %q", err, string(output))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns a list of all mounted filesystems. todo
|
||||||
|
func (mounter *Mounter) List() ([]MountPoint, error) {
|
||||||
|
return []MountPoint{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLikelyNotMountPoint determines if a directory is not a mountpoint.
|
||||||
|
func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||||
|
stat, err := os.Lstat(file)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if stat.Mode()&os.ModeSymlink != 0 {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMountRefs : empty implementation here since there is no place to query all mount points on Windows
|
||||||
|
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
|
||||||
|
windowsPath := NormalizeWindowsPath(pathname)
|
||||||
|
pathExists, pathErr := PathExists(windowsPath)
|
||||||
|
if !pathExists {
|
||||||
|
return []string{}, nil
|
||||||
|
} else if IsCorruptedMnt(pathErr) {
|
||||||
|
klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", windowsPath)
|
||||||
|
return []string{}, nil
|
||||||
|
} else if pathErr != nil {
|
||||||
|
return nil, fmt.Errorf("error checking path %s: %v", windowsPath, pathErr)
|
||||||
|
}
|
||||||
|
return []string{pathname}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
||||||
|
// Try to mount the disk
|
||||||
|
klog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target)
|
||||||
|
|
||||||
|
if err := ValidateDiskNumber(source); err != nil {
|
||||||
|
klog.Errorf("diskMount: formatAndMount failed, err: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(fstype) == 0 {
|
||||||
|
// Use 'NTFS' as the default
|
||||||
|
fstype = "NTFS"
|
||||||
|
}
|
||||||
|
|
||||||
|
// format disk if it is unformatted(raw)
|
||||||
|
cmd := fmt.Sprintf("Get-Disk -Number %s | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru"+
|
||||||
|
" | New-Partition -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false", source, fstype)
|
||||||
|
if output, err := mounter.Exec.Command("powershell", "/c", cmd).CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("diskMount: format disk failed, error: %v, output: %q", err, string(output))
|
||||||
|
}
|
||||||
|
klog.V(4).Infof("diskMount: Disk successfully formatted, disk: %q, fstype: %q", source, fstype)
|
||||||
|
|
||||||
|
volumeIds, err := listVolumesOnDisk(source)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
driverPath := volumeIds[0]
|
||||||
|
target = NormalizeWindowsPath(target)
|
||||||
|
output, err := mounter.Exec.Command("cmd", "/c", "mklink", "/D", target, driverPath).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("mklink(%s, %s) failed: %v, output: %q", target, driverPath, err, string(output))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
klog.V(2).Infof("formatAndMount disk(%s) fstype(%s) on(%s) with output(%s) successfully", driverPath, fstype, target, string(output))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListVolumesOnDisk - returns back list of volumes(volumeIDs) in the disk (requested in diskID).
|
||||||
|
func listVolumesOnDisk(diskID string) (volumeIDs []string, err error) {
|
||||||
|
cmd := fmt.Sprintf("(Get-Disk -DeviceId %s | Get-Partition | Get-Volume).UniqueId", diskID)
|
||||||
|
output, err := exec.Command("powershell", "/c", cmd).CombinedOutput()
|
||||||
|
klog.V(4).Infof("listVolumesOnDisk id from %s: %s", diskID, string(output))
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, fmt.Errorf("error list volumes on disk. cmd: %s, output: %s, error: %v", cmd, string(output), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
volumeIds := strings.Split(strings.TrimSpace(string(output)), "\r\n")
|
||||||
|
return volumeIds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAllParentLinks walks all symbolic links and return all the parent targets recursively
|
||||||
|
func getAllParentLinks(path string) ([]string, error) {
|
||||||
|
const maxIter = 255
|
||||||
|
links := []string{}
|
||||||
|
for {
|
||||||
|
links = append(links, path)
|
||||||
|
if len(links) > maxIter {
|
||||||
|
return links, fmt.Errorf("unexpected length of parent links: %v", links)
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Lstat(path)
|
||||||
|
if err != nil {
|
||||||
|
return links, fmt.Errorf("Lstat: %v", err)
|
||||||
|
}
|
||||||
|
if fi.Mode()&os.ModeSymlink == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
path, err = os.Readlink(path)
|
||||||
|
if err != nil {
|
||||||
|
return links, fmt.Errorf("Readlink error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return links, nil
|
||||||
|
}
|
||||||
10
vendor/k8s.io/utils/nsenter/OWNERS
generated
vendored
Normal file
10
vendor/k8s.io/utils/nsenter/OWNERS
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# See the OWNERS docs at https://go.k8s.io/owners
|
||||||
|
|
||||||
|
reviewers:
|
||||||
|
- jsafrane
|
||||||
|
- msau42
|
||||||
|
- cofyc
|
||||||
|
approvers:
|
||||||
|
- jsafrane
|
||||||
|
- msau42
|
||||||
|
- cofyc
|
||||||
4
vendor/k8s.io/utils/nsenter/README.md
generated
vendored
Normal file
4
vendor/k8s.io/utils/nsenter/README.md
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# NSEnter
|
||||||
|
|
||||||
|
This package provides interfaces for executing and interacting with processes
|
||||||
|
running within a namespace.
|
||||||
258
vendor/k8s.io/utils/nsenter/nsenter.go
generated
vendored
Normal file
258
vendor/k8s.io/utils/nsenter/nsenter.go
generated
vendored
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package nsenter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
"k8s.io/utils/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultHostRootFsPath is path to host's filesystem mounted into container
|
||||||
|
// with kubelet.
|
||||||
|
DefaultHostRootFsPath = "/rootfs"
|
||||||
|
// mountNsPath is the default mount namespace of the host
|
||||||
|
mountNsPath = "/proc/1/ns/mnt"
|
||||||
|
// nsenterPath is the default nsenter command
|
||||||
|
nsenterPath = "nsenter"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Nsenter is a type alias for backward compatibility
|
||||||
|
type Nsenter = NSEnter
|
||||||
|
|
||||||
|
// NSEnter is part of experimental support for running the kubelet
|
||||||
|
// in a container.
|
||||||
|
//
|
||||||
|
// NSEnter requires:
|
||||||
|
//
|
||||||
|
// 1. Docker >= 1.6 due to the dependency on the slave propagation mode
|
||||||
|
// of the bind-mount of the kubelet root directory in the container.
|
||||||
|
// Docker 1.5 used a private propagation mode for bind-mounts, so mounts
|
||||||
|
// performed in the host's mount namespace do not propagate out to the
|
||||||
|
// bind-mount in this docker version.
|
||||||
|
// 2. The host's root filesystem must be available at /rootfs
|
||||||
|
// 3. The nsenter binary must be on the Kubelet process' PATH in the container's
|
||||||
|
// filesystem.
|
||||||
|
// 4. The Kubelet process must have CAP_SYS_ADMIN (required by nsenter); at
|
||||||
|
// the present, this effectively means that the kubelet is running in a
|
||||||
|
// privileged container.
|
||||||
|
// 5. The volume path used by the Kubelet must be the same inside and outside
|
||||||
|
// the container and be writable by the container (to initialize volume)
|
||||||
|
// contents. TODO: remove this requirement.
|
||||||
|
// 6. The host image must have "mount", "findmnt", "umount", "stat", "touch",
|
||||||
|
// "mkdir", "ls", "sh" and "chmod" binaries in /bin, /usr/sbin, or /usr/bin
|
||||||
|
// 7. The host image should have systemd-run in /bin, /usr/sbin, or /usr/bin if
|
||||||
|
// systemd is installed/enabled in the operating system.
|
||||||
|
// For more information about mount propagation modes, see:
|
||||||
|
// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
|
||||||
|
type NSEnter struct {
|
||||||
|
// a map of commands to their paths on the host filesystem
|
||||||
|
paths map[string]string
|
||||||
|
|
||||||
|
// Path to the host filesystem, typically "/rootfs". Used only for testing.
|
||||||
|
hostRootFsPath string
|
||||||
|
|
||||||
|
// Exec implementation
|
||||||
|
executor exec.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNsenter constructs a new instance of NSEnter
|
||||||
|
func NewNsenter(hostRootFsPath string, executor exec.Interface) (*NSEnter, error) {
|
||||||
|
ne := &NSEnter{
|
||||||
|
hostRootFsPath: hostRootFsPath,
|
||||||
|
executor: executor,
|
||||||
|
}
|
||||||
|
if err := ne.initPaths(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ne, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ne *NSEnter) initPaths() error {
|
||||||
|
ne.paths = map[string]string{}
|
||||||
|
binaries := []string{
|
||||||
|
"mount",
|
||||||
|
"findmnt",
|
||||||
|
"umount",
|
||||||
|
"systemd-run",
|
||||||
|
"stat",
|
||||||
|
"touch",
|
||||||
|
"mkdir",
|
||||||
|
"sh",
|
||||||
|
"chmod",
|
||||||
|
"realpath",
|
||||||
|
}
|
||||||
|
// search for the required commands in other locations besides /usr/bin
|
||||||
|
for _, binary := range binaries {
|
||||||
|
// check for binary under the following directories
|
||||||
|
for _, path := range []string{"/", "/bin", "/usr/sbin", "/usr/bin"} {
|
||||||
|
binPath := filepath.Join(path, binary)
|
||||||
|
if _, err := os.Stat(filepath.Join(ne.hostRootFsPath, binPath)); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ne.paths[binary] = binPath
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// systemd-run is optional, bailout if we don't find any of the other binaries
|
||||||
|
if ne.paths[binary] == "" && binary != "systemd-run" {
|
||||||
|
return fmt.Errorf("unable to find %v", binary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes nsenter commands in hostProcMountNsPath mount namespace
|
||||||
|
func (ne *NSEnter) Exec(cmd string, args []string) exec.Cmd {
|
||||||
|
hostProcMountNsPath := filepath.Join(ne.hostRootFsPath, mountNsPath)
|
||||||
|
fullArgs := append([]string{fmt.Sprintf("--mount=%s", hostProcMountNsPath), "--"},
|
||||||
|
append([]string{ne.AbsHostPath(cmd)}, args...)...)
|
||||||
|
klog.V(5).Infof("Running nsenter command: %v %v", nsenterPath, fullArgs)
|
||||||
|
return ne.executor.Command(nsenterPath, fullArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns a command wrapped with nsenter
|
||||||
|
func (ne *NSEnter) Command(cmd string, args ...string) exec.Cmd {
|
||||||
|
return ne.Exec(cmd, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommandContext returns a CommandContext wrapped with nsenter
|
||||||
|
func (ne *NSEnter) CommandContext(ctx context.Context, cmd string, args ...string) exec.Cmd {
|
||||||
|
hostProcMountNsPath := filepath.Join(ne.hostRootFsPath, mountNsPath)
|
||||||
|
fullArgs := append([]string{fmt.Sprintf("--mount=%s", hostProcMountNsPath), "--"},
|
||||||
|
append([]string{ne.AbsHostPath(cmd)}, args...)...)
|
||||||
|
klog.V(5).Infof("Running nsenter command: %v %v", nsenterPath, fullArgs)
|
||||||
|
return ne.executor.CommandContext(ctx, nsenterPath, fullArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookPath returns a LookPath wrapped with nsenter
|
||||||
|
func (ne *NSEnter) LookPath(file string) (string, error) {
|
||||||
|
return "", fmt.Errorf("not implemented, error looking up : %s", file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsHostPath returns the absolute runnable path for a specified command
|
||||||
|
func (ne *NSEnter) AbsHostPath(command string) string {
|
||||||
|
path, ok := ne.paths[command]
|
||||||
|
if !ok {
|
||||||
|
return command
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsSystemd checks whether command systemd-run exists
|
||||||
|
func (ne *NSEnter) SupportsSystemd() (string, bool) {
|
||||||
|
systemdRunPath, ok := ne.paths["systemd-run"]
|
||||||
|
return systemdRunPath, ok && systemdRunPath != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvalSymlinks returns the path name on the host after evaluating symlinks on the
|
||||||
|
// host.
|
||||||
|
// mustExist makes EvalSymlinks to return error when the path does not
|
||||||
|
// exist. When it's false, it evaluates symlinks of the existing part and
|
||||||
|
// blindly adds the non-existing part:
|
||||||
|
// pathname: /mnt/volume/non/existing/directory
|
||||||
|
// /mnt/volume exists
|
||||||
|
// non/existing/directory does not exist
|
||||||
|
// -> It resolves symlinks in /mnt/volume to say /mnt/foo and returns
|
||||||
|
// /mnt/foo/non/existing/directory.
|
||||||
|
//
|
||||||
|
// BEWARE! EvalSymlinks is not able to detect symlink looks with mustExist=false!
|
||||||
|
// If /tmp/link is symlink to /tmp/link, EvalSymlinks(/tmp/link/foo) returns /tmp/link/foo.
|
||||||
|
func (ne *NSEnter) EvalSymlinks(pathname string, mustExist bool) (string, error) {
|
||||||
|
var args []string
|
||||||
|
if mustExist {
|
||||||
|
// "realpath -e: all components of the path must exist"
|
||||||
|
args = []string{"-e", pathname}
|
||||||
|
} else {
|
||||||
|
// "realpath -m: no path components need exist or be a directory"
|
||||||
|
args = []string{"-m", pathname}
|
||||||
|
}
|
||||||
|
outBytes, err := ne.Exec("realpath", args).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
klog.Infof("failed to resolve symbolic links on %s: %v", pathname, err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(outBytes)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubeletPath returns the path name that can be accessed by containerized
|
||||||
|
// kubelet. It is recommended to resolve symlinks on the host by EvalSymlinks
|
||||||
|
// before calling this function
|
||||||
|
func (ne *NSEnter) KubeletPath(pathname string) string {
|
||||||
|
return filepath.Join(ne.hostRootFsPath, pathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFakeNsenter returns a NSEnter that does not run "nsenter --mount=... --",
|
||||||
|
// but runs everything in the same mount namespace as the unit test binary.
|
||||||
|
// rootfsPath is supposed to be a symlink, e.g. /tmp/xyz/rootfs -> /.
|
||||||
|
// This fake NSEnter is enough for most operations, e.g. to resolve symlinks,
|
||||||
|
// but it's not enough to call /bin/mount - unit tests don't run as root.
|
||||||
|
func NewFakeNsenter(rootfsPath string) (*NSEnter, error) {
|
||||||
|
executor := &fakeExec{
|
||||||
|
rootfsPath: rootfsPath,
|
||||||
|
}
|
||||||
|
// prepare /rootfs/bin, usr/bin and usr/sbin
|
||||||
|
bin := filepath.Join(rootfsPath, "bin")
|
||||||
|
if err := os.Symlink("/bin", bin); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
usr := filepath.Join(rootfsPath, "usr")
|
||||||
|
if err := os.Mkdir(usr, 0755); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
usrbin := filepath.Join(usr, "bin")
|
||||||
|
if err := os.Symlink("/usr/bin", usrbin); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
usrsbin := filepath.Join(usr, "sbin")
|
||||||
|
if err := os.Symlink("/usr/sbin", usrsbin); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewNsenter(rootfsPath, executor)
|
||||||
|
}
|
||||||
|
|
||||||
|
type fakeExec struct {
|
||||||
|
rootfsPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fakeExec) Command(cmd string, args ...string) exec.Cmd {
|
||||||
|
// This will intentionaly panic if NSEnter does not provide enough arguments.
|
||||||
|
realCmd := args[2]
|
||||||
|
realArgs := args[3:]
|
||||||
|
return exec.New().Command(realCmd, realArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fakeExec) LookPath(file string) (string, error) {
|
||||||
|
return "", errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fakeExec) CommandContext(ctx context.Context, cmd string, args ...string) exec.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ exec.Interface = fakeExec{}
|
||||||
|
var _ exec.Interface = &NSEnter{}
|
||||||
79
vendor/k8s.io/utils/nsenter/nsenter_unsupported.go
generated
vendored
Normal file
79
vendor/k8s.io/utils/nsenter/nsenter_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// +build !linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package nsenter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/utils/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultHostRootFsPath is path to host's filesystem mounted into container
|
||||||
|
// with kubelet.
|
||||||
|
DefaultHostRootFsPath = "/rootfs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Nsenter is a type alias for backward compatibility
|
||||||
|
type Nsenter = NSEnter
|
||||||
|
|
||||||
|
// NSEnter is part of experimental support for running the kubelet
|
||||||
|
// in a container.
|
||||||
|
type NSEnter struct {
|
||||||
|
// a map of commands to their paths on the host filesystem
|
||||||
|
Paths map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNsenter constructs a new instance of NSEnter
|
||||||
|
func NewNsenter(hostRootFsPath string, executor exec.Interface) (*Nsenter, error) {
|
||||||
|
return &Nsenter{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes nsenter commands in hostProcMountNsPath mount namespace
|
||||||
|
func (ne *NSEnter) Exec(cmd string, args []string) exec.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsHostPath returns the absolute runnable path for a specified command
|
||||||
|
func (ne *NSEnter) AbsHostPath(command string) string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsSystemd checks whether command systemd-run exists
|
||||||
|
func (ne *NSEnter) SupportsSystemd() (string, bool) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns a command wrapped with nenter
|
||||||
|
func (ne *NSEnter) Command(cmd string, args ...string) exec.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommandContext returns a CommandContext wrapped with nsenter
|
||||||
|
func (ne *NSEnter) CommandContext(ctx context.Context, cmd string, args ...string) exec.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookPath returns a LookPath wrapped with nsenter
|
||||||
|
func (ne *NSEnter) LookPath(file string) (string, error) {
|
||||||
|
return "", fmt.Errorf("not implemented, error looking up : %s", file)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ exec.Interface = &NSEnter{}
|
||||||
14
vendor/modules.txt
vendored
14
vendor/modules.txt
vendored
@ -648,6 +648,8 @@ k8s.io/client-go/util/homedir
|
|||||||
k8s.io/client-go/util/keyutil
|
k8s.io/client-go/util/keyutil
|
||||||
k8s.io/client-go/util/retry
|
k8s.io/client-go/util/retry
|
||||||
k8s.io/client-go/util/workqueue
|
k8s.io/client-go/util/workqueue
|
||||||
|
# k8s.io/cloud-provider v0.20.0 => k8s.io/cloud-provider v0.20.0
|
||||||
|
k8s.io/cloud-provider
|
||||||
# k8s.io/component-base v0.20.0 => k8s.io/component-base v0.20.0
|
# k8s.io/component-base v0.20.0 => k8s.io/component-base v0.20.0
|
||||||
k8s.io/component-base/cli/flag
|
k8s.io/component-base/cli/flag
|
||||||
k8s.io/component-base/config
|
k8s.io/component-base/config
|
||||||
@ -695,11 +697,20 @@ k8s.io/kubernetes/pkg/features
|
|||||||
k8s.io/kubernetes/pkg/fieldpath
|
k8s.io/kubernetes/pkg/fieldpath
|
||||||
k8s.io/kubernetes/pkg/kubelet/apis/config
|
k8s.io/kubernetes/pkg/kubelet/apis/config
|
||||||
k8s.io/kubernetes/pkg/kubelet/types
|
k8s.io/kubernetes/pkg/kubelet/types
|
||||||
|
k8s.io/kubernetes/pkg/proxy/util
|
||||||
k8s.io/kubernetes/pkg/security/apparmor
|
k8s.io/kubernetes/pkg/security/apparmor
|
||||||
k8s.io/kubernetes/pkg/util/hash
|
k8s.io/kubernetes/pkg/util/hash
|
||||||
k8s.io/kubernetes/pkg/util/labels
|
k8s.io/kubernetes/pkg/util/labels
|
||||||
k8s.io/kubernetes/pkg/util/parsers
|
k8s.io/kubernetes/pkg/util/parsers
|
||||||
|
k8s.io/kubernetes/pkg/util/sysctl
|
||||||
k8s.io/kubernetes/pkg/util/taints
|
k8s.io/kubernetes/pkg/util/taints
|
||||||
|
k8s.io/kubernetes/pkg/volume
|
||||||
|
k8s.io/kubernetes/pkg/volume/util/fs
|
||||||
|
k8s.io/kubernetes/pkg/volume/util/fsquota
|
||||||
|
k8s.io/kubernetes/pkg/volume/util/fsquota/common
|
||||||
|
k8s.io/kubernetes/pkg/volume/util/hostutil
|
||||||
|
k8s.io/kubernetes/pkg/volume/util/recyclerclient
|
||||||
|
k8s.io/kubernetes/pkg/volume/util/subpath
|
||||||
k8s.io/kubernetes/test/e2e/framework
|
k8s.io/kubernetes/test/e2e/framework
|
||||||
k8s.io/kubernetes/test/e2e/framework/auth
|
k8s.io/kubernetes/test/e2e/framework/auth
|
||||||
k8s.io/kubernetes/test/e2e/framework/config
|
k8s.io/kubernetes/test/e2e/framework/config
|
||||||
@ -718,6 +729,8 @@ k8s.io/kubernetes/test/e2e/perftype
|
|||||||
k8s.io/kubernetes/test/e2e/storage/utils
|
k8s.io/kubernetes/test/e2e/storage/utils
|
||||||
k8s.io/kubernetes/test/utils
|
k8s.io/kubernetes/test/utils
|
||||||
k8s.io/kubernetes/test/utils/image
|
k8s.io/kubernetes/test/utils/image
|
||||||
|
# k8s.io/mount-utils v0.0.0 => k8s.io/mount-utils v0.21.0-alpha.0
|
||||||
|
k8s.io/mount-utils
|
||||||
# k8s.io/utils v0.0.0-20201110183641-67b214c5f920
|
# k8s.io/utils v0.0.0-20201110183641-67b214c5f920
|
||||||
## explicit
|
## explicit
|
||||||
k8s.io/utils/buffer
|
k8s.io/utils/buffer
|
||||||
@ -727,6 +740,7 @@ k8s.io/utils/io
|
|||||||
k8s.io/utils/keymutex
|
k8s.io/utils/keymutex
|
||||||
k8s.io/utils/mount
|
k8s.io/utils/mount
|
||||||
k8s.io/utils/net
|
k8s.io/utils/net
|
||||||
|
k8s.io/utils/nsenter
|
||||||
k8s.io/utils/path
|
k8s.io/utils/path
|
||||||
k8s.io/utils/pointer
|
k8s.io/utils/pointer
|
||||||
k8s.io/utils/trace
|
k8s.io/utils/trace
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user