mirror of
https://github.com/kubernetes-sigs/descheduler.git
synced 2026-01-28 14:41:10 +01:00
Remove old vendor, glide.lock/yaml
This commit is contained in:
8
vendor/k8s.io/utils/.travis.yml
generated
vendored
8
vendor/k8s.io/utils/.travis.yml
generated
vendored
@@ -1,8 +0,0 @@
|
||||
language: go
|
||||
dist: xenial
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
go_import_path: k8s.io/utils
|
||||
script:
|
||||
- make verify
|
||||
9
vendor/k8s.io/utils/CONTRIBUTING.md
generated
vendored
9
vendor/k8s.io/utils/CONTRIBUTING.md
generated
vendored
@@ -1,9 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
Thanks for taking the time to join our community and start contributing!
|
||||
|
||||
The [Contributor Guide](https://github.com/kubernetes/community/blob/master/contributors/guide/README.md)
|
||||
provides detailed instructions on how to get your ideas and bug fixes seen and accepted.
|
||||
|
||||
Please remember to sign the [CNCF CLA](https://github.com/kubernetes/community/blob/master/CLA.md) and
|
||||
read and observe the [Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
||||
31
vendor/k8s.io/utils/HOWTOMOVE.md
generated
vendored
31
vendor/k8s.io/utils/HOWTOMOVE.md
generated
vendored
@@ -1,31 +0,0 @@
|
||||
# How to move a utility pkg from other kubernetes repos
|
||||
|
||||
It has 2 steps to move a pkg from other Kubernetes repos to `k8s.io/utils` repo:
|
||||
- copy the pkg to `k8s.io/utils` repo
|
||||
- update the import paths and `vendor/` in the repos that refer this pkg
|
||||
|
||||
## Copy the pkg to `k8s.io/utils` repo
|
||||
|
||||
Copying should preserve all the git history associated with it.
|
||||
[Here](http://gbayer.com/development/moving-files-from-one-git-repository-to-another-preserving-history/) is a working approach.
|
||||
Note: You may need to use `--allow-unrelated-histories` if you get error when running `git pull` following the post above.
|
||||
|
||||
Then, you may need to restructure the package to make sure it has the following structure.
|
||||
|
||||
.
|
||||
├── doc.go # Description for this package
|
||||
├── <utilname1>.go # utility go file
|
||||
├── <utilname>_test.go # go unit tests
|
||||
└── testing # All the testing framework
|
||||
└── fake_<utilname>.go # Testing framework go file
|
||||
|
||||
[#5](https://github.com/kubernetes/utils/pull/5) is an example for this step.
|
||||
|
||||
## Update the repos that refer the pkg
|
||||
|
||||
You should update the import paths.
|
||||
Then follow [this doc](https://github.com/kubernetes/community/blob/master/contributors/devel/godep.md) to update `vendor/` and `Godeps/`.
|
||||
|
||||
You may want to run `make bazel-test` to make sure all new references work.
|
||||
|
||||
[kubernetes/kubernetes#49234](https://github.com/kubernetes/kubernetes/pull/49234) is an example for this step.
|
||||
202
vendor/k8s.io/utils/LICENSE
generated
vendored
202
vendor/k8s.io/utils/LICENSE
generated
vendored
@@ -1,202 +0,0 @@
|
||||
|
||||
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.
|
||||
33
vendor/k8s.io/utils/Makefile
generated
vendored
33
vendor/k8s.io/utils/Makefile
generated
vendored
@@ -1,33 +0,0 @@
|
||||
# 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.
|
||||
|
||||
.PHONY: verify
|
||||
verify: verify-fmt verify-lint vet
|
||||
GO111MODULE=on go test -v -race ./...
|
||||
|
||||
.PHONY: verify-fmt
|
||||
verify-fmt:
|
||||
GO111MODULE=on ./hack/verify-gofmt.sh
|
||||
|
||||
.PHONY: verify-lint
|
||||
verify-lint:
|
||||
GO111MODULE=on ./hack/verify-golint.sh
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
GO111MODULE=on ./hack/verify-govet.sh
|
||||
|
||||
.PHONY: update-fmt
|
||||
update-fmt:
|
||||
gofmt -s -w .
|
||||
21
vendor/k8s.io/utils/OWNERS
generated
vendored
21
vendor/k8s.io/utils/OWNERS
generated
vendored
@@ -1,21 +0,0 @@
|
||||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
approvers:
|
||||
- apelisse
|
||||
- dashpole
|
||||
- deads2k
|
||||
- lavalamp
|
||||
- mengqiy
|
||||
- thockin
|
||||
- dims
|
||||
reviewers:
|
||||
- apelisse
|
||||
- dashpole
|
||||
- deads2k
|
||||
- lavalamp
|
||||
- mengqiy
|
||||
- thockin
|
||||
- andrewsykim
|
||||
- cheftako
|
||||
- mcrute
|
||||
- dims
|
||||
69
vendor/k8s.io/utils/README.md
generated
vendored
69
vendor/k8s.io/utils/README.md
generated
vendored
@@ -1,69 +0,0 @@
|
||||
# Utils
|
||||
|
||||
[![Build Status]](https://travis-ci.org/kubernetes/utils) [](https://godoc.org/k8s.io/utils)
|
||||
|
||||
A set of Go libraries that provide low-level,
|
||||
kubernetes-independent packages supplementing the [Go
|
||||
standard libs].
|
||||
|
||||
## Purpose
|
||||
|
||||
As Kubernetes grows and spins functionality out of its
|
||||
[core] and into cooperating repositories like
|
||||
[apiserver], [kubectl], [kubeadm], etc., the need
|
||||
arises for leaf repositories to house shared code and
|
||||
avoid cycles in repository relationships.
|
||||
|
||||
This repository is intended to hold shared utilities
|
||||
with no Kubernetes dependence that may be of interest
|
||||
to any Go project. See these [instructions for moving]
|
||||
an existing package to this repository.
|
||||
|
||||
|
||||
## Criteria for adding code here
|
||||
|
||||
- Used by multiple Kubernetes repositories.
|
||||
|
||||
- Full unit test coverage.
|
||||
|
||||
- Go tools compliant (`go get`, `go test`, etc.).
|
||||
|
||||
- Complex enough to be worth vendoring, rather than copying.
|
||||
|
||||
- Stable, or backward compatible, API.
|
||||
|
||||
- _No dependence on any Kubernetes repository_.
|
||||
|
||||
## Libraries
|
||||
|
||||
- [Exec](/exec) provides an interface for `os/exec`. It makes it easier
|
||||
to mock and replace in tests, especially with
|
||||
the [FakeExec](exec/testing/fake_exec.go) struct.
|
||||
|
||||
- [Temp](/temp) provides an interface to create temporary directories. It also
|
||||
provides a [FakeDir](temp/temptest) implementation to replace in tests.
|
||||
|
||||
- [Clock](/clock) provides an interface for time-based operations. It allows
|
||||
mocking time for testing.
|
||||
|
||||
- [Pointer](/pointer) provides some functions for pointer-based operations.
|
||||
|
||||
- [Io](/io) provides interfaces for working with file IO. Currently it provides
|
||||
functionality for consistently reading a file.
|
||||
|
||||
- [NSEnter](/nsenter) provides interfaces for executing and interacting with
|
||||
processes running within a namespace.
|
||||
|
||||
[Build Status]: https://travis-ci.org/kubernetes/utils.svg?branch=master
|
||||
[Go standard libs]: https://golang.org/pkg/#stdlib
|
||||
[api]: https://github.com/kubernetes/api
|
||||
[apiserver]: https://github.com/kubernetes/apiserver
|
||||
[core]: https://github.com/kubernetes/kubernetes
|
||||
[ingress]: https://github.com/kubernetes/ingress
|
||||
[kubeadm]: https://github.com/kubernetes/kubeadm
|
||||
[kubectl]: https://github.com/kubernetes/kubectl
|
||||
[instructions for moving]: ./HOWTOMOVE.md
|
||||
|
||||
## Contributing
|
||||
|
||||
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for instructions on how to contribute.
|
||||
13
vendor/k8s.io/utils/SECURITY_CONTACTS
generated
vendored
13
vendor/k8s.io/utils/SECURITY_CONTACTS
generated
vendored
@@ -1,13 +0,0 @@
|
||||
# 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/
|
||||
|
||||
liggitt
|
||||
72
vendor/k8s.io/utils/buffer/ring_growing.go
generated
vendored
72
vendor/k8s.io/utils/buffer/ring_growing.go
generated
vendored
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
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 buffer
|
||||
|
||||
// RingGrowing is a growing ring buffer.
|
||||
// Not thread safe.
|
||||
type RingGrowing struct {
|
||||
data []interface{}
|
||||
n int // Size of Data
|
||||
beg int // First available element
|
||||
readable int // Number of data items available
|
||||
}
|
||||
|
||||
// NewRingGrowing constructs a new RingGrowing instance with provided parameters.
|
||||
func NewRingGrowing(initialSize int) *RingGrowing {
|
||||
return &RingGrowing{
|
||||
data: make([]interface{}, initialSize),
|
||||
n: initialSize,
|
||||
}
|
||||
}
|
||||
|
||||
// ReadOne reads (consumes) first item from the buffer if it is available, otherwise returns false.
|
||||
func (r *RingGrowing) ReadOne() (data interface{}, ok bool) {
|
||||
if r.readable == 0 {
|
||||
return nil, false
|
||||
}
|
||||
r.readable--
|
||||
element := r.data[r.beg]
|
||||
r.data[r.beg] = nil // Remove reference to the object to help GC
|
||||
if r.beg == r.n-1 {
|
||||
// Was the last element
|
||||
r.beg = 0
|
||||
} else {
|
||||
r.beg++
|
||||
}
|
||||
return element, true
|
||||
}
|
||||
|
||||
// WriteOne adds an item to the end of the buffer, growing it if it is full.
|
||||
func (r *RingGrowing) WriteOne(data interface{}) {
|
||||
if r.readable == r.n {
|
||||
// Time to grow
|
||||
newN := r.n * 2
|
||||
newData := make([]interface{}, newN)
|
||||
to := r.beg + r.readable
|
||||
if to <= r.n {
|
||||
copy(newData, r.data[r.beg:to])
|
||||
} else {
|
||||
copied := copy(newData, r.data[r.beg:])
|
||||
copy(newData[copied:], r.data[:(to%r.n)])
|
||||
}
|
||||
r.beg = 0
|
||||
r.data = newData
|
||||
r.n = newN
|
||||
}
|
||||
r.data[(r.readable+r.beg)%r.n] = data
|
||||
r.readable++
|
||||
}
|
||||
50
vendor/k8s.io/utils/buffer/ring_growing_test.go
generated
vendored
50
vendor/k8s.io/utils/buffer/ring_growing_test.go
generated
vendored
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
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 buffer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGrowth(t *testing.T) {
|
||||
t.Parallel()
|
||||
x := 10
|
||||
g := NewRingGrowing(1)
|
||||
for i := 0; i < x; i++ {
|
||||
assert.Equal(t, i, g.readable)
|
||||
g.WriteOne(i)
|
||||
}
|
||||
read := 0
|
||||
for g.readable > 0 {
|
||||
v, ok := g.ReadOne()
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, read, v)
|
||||
read++
|
||||
}
|
||||
assert.Equalf(t, x, read, "expected to have read %d items: %d", x, read)
|
||||
assert.Zerof(t, g.readable, "expected readable to be zero: %d", g.readable)
|
||||
assert.Equalf(t, 16, g.n, "expected N to be 16: %d", g.n)
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
g := NewRingGrowing(1)
|
||||
_, ok := g.ReadOne()
|
||||
assert.False(t, ok)
|
||||
}
|
||||
97
vendor/k8s.io/utils/clock/clock.go
generated
vendored
97
vendor/k8s.io/utils/clock/clock.go
generated
vendored
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
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 clock
|
||||
|
||||
import "time"
|
||||
|
||||
// Clock allows for injecting fake or real clocks into code that
|
||||
// needs to do arbitrary things based on time.
|
||||
type Clock interface {
|
||||
Now() time.Time
|
||||
Since(time.Time) time.Duration
|
||||
After(d time.Duration) <-chan time.Time
|
||||
NewTimer(d time.Duration) Timer
|
||||
Sleep(d time.Duration)
|
||||
Tick(d time.Duration) <-chan time.Time
|
||||
}
|
||||
|
||||
var _ = Clock(RealClock{})
|
||||
|
||||
// RealClock really calls time.Now()
|
||||
type RealClock struct{}
|
||||
|
||||
// Now returns the current time.
|
||||
func (RealClock) Now() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// Since returns time since the specified timestamp.
|
||||
func (RealClock) Since(ts time.Time) time.Duration {
|
||||
return time.Since(ts)
|
||||
}
|
||||
|
||||
// After is the same as time.After(d).
|
||||
func (RealClock) After(d time.Duration) <-chan time.Time {
|
||||
return time.After(d)
|
||||
}
|
||||
|
||||
// NewTimer is the same as time.NewTimer(d)
|
||||
func (RealClock) NewTimer(d time.Duration) Timer {
|
||||
return &realTimer{
|
||||
timer: time.NewTimer(d),
|
||||
}
|
||||
}
|
||||
|
||||
// Tick is the same as time.Tick(d)
|
||||
func (RealClock) Tick(d time.Duration) <-chan time.Time {
|
||||
return time.Tick(d)
|
||||
}
|
||||
|
||||
// Sleep is the same as time.Sleep(d)
|
||||
func (RealClock) Sleep(d time.Duration) {
|
||||
time.Sleep(d)
|
||||
}
|
||||
|
||||
// Timer allows for injecting fake or real timers into code that
|
||||
// needs to do arbitrary things based on time.
|
||||
type Timer interface {
|
||||
C() <-chan time.Time
|
||||
Stop() bool
|
||||
Reset(d time.Duration) bool
|
||||
}
|
||||
|
||||
var _ = Timer(&realTimer{})
|
||||
|
||||
// realTimer is backed by an actual time.Timer.
|
||||
type realTimer struct {
|
||||
timer *time.Timer
|
||||
}
|
||||
|
||||
// C returns the underlying timer's channel.
|
||||
func (r *realTimer) C() <-chan time.Time {
|
||||
return r.timer.C
|
||||
}
|
||||
|
||||
// Stop calls Stop() on the underlying timer.
|
||||
func (r *realTimer) Stop() bool {
|
||||
return r.timer.Stop()
|
||||
}
|
||||
|
||||
// Reset calls Reset() on the underlying timer.
|
||||
func (r *realTimer) Reset(d time.Duration) bool {
|
||||
return r.timer.Reset(d)
|
||||
}
|
||||
274
vendor/k8s.io/utils/clock/testing/fake_clock.go
generated
vendored
274
vendor/k8s.io/utils/clock/testing/fake_clock.go
generated
vendored
@@ -1,274 +0,0 @@
|
||||
/*
|
||||
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 testing
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/utils/clock"
|
||||
)
|
||||
|
||||
var (
|
||||
_ = clock.Clock(&FakeClock{})
|
||||
_ = clock.Clock(&IntervalClock{})
|
||||
)
|
||||
|
||||
// FakeClock implements clock.Clock, but returns an arbitrary time.
|
||||
type FakeClock struct {
|
||||
lock sync.RWMutex
|
||||
time time.Time
|
||||
|
||||
// waiters are waiting for the fake time to pass their specified time
|
||||
waiters []*fakeClockWaiter
|
||||
}
|
||||
|
||||
type fakeClockWaiter struct {
|
||||
targetTime time.Time
|
||||
stepInterval time.Duration
|
||||
skipIfBlocked bool
|
||||
destChan chan time.Time
|
||||
fired bool
|
||||
}
|
||||
|
||||
// NewFakeClock constructs a fake clock set to the provided time.
|
||||
func NewFakeClock(t time.Time) *FakeClock {
|
||||
return &FakeClock{
|
||||
time: t,
|
||||
}
|
||||
}
|
||||
|
||||
// Now returns f's time.
|
||||
func (f *FakeClock) Now() time.Time {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
return f.time
|
||||
}
|
||||
|
||||
// Since returns time since the time in f.
|
||||
func (f *FakeClock) Since(ts time.Time) time.Duration {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
return f.time.Sub(ts)
|
||||
}
|
||||
|
||||
// After is the fake version of time.After(d).
|
||||
func (f *FakeClock) After(d time.Duration) <-chan time.Time {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
stopTime := f.time.Add(d)
|
||||
ch := make(chan time.Time, 1) // Don't block!
|
||||
f.waiters = append(f.waiters, &fakeClockWaiter{
|
||||
targetTime: stopTime,
|
||||
destChan: ch,
|
||||
})
|
||||
return ch
|
||||
}
|
||||
|
||||
// NewTimer constructs a fake timer, akin to time.NewTimer(d).
|
||||
func (f *FakeClock) NewTimer(d time.Duration) clock.Timer {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
stopTime := f.time.Add(d)
|
||||
ch := make(chan time.Time, 1) // Don't block!
|
||||
timer := &fakeTimer{
|
||||
fakeClock: f,
|
||||
waiter: fakeClockWaiter{
|
||||
targetTime: stopTime,
|
||||
destChan: ch,
|
||||
},
|
||||
}
|
||||
f.waiters = append(f.waiters, &timer.waiter)
|
||||
return timer
|
||||
}
|
||||
|
||||
// Tick constructs a fake ticker, akin to time.Tick
|
||||
func (f *FakeClock) Tick(d time.Duration) <-chan time.Time {
|
||||
if d <= 0 {
|
||||
return nil
|
||||
}
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
tickTime := f.time.Add(d)
|
||||
ch := make(chan time.Time, 1) // hold one tick
|
||||
f.waiters = append(f.waiters, &fakeClockWaiter{
|
||||
targetTime: tickTime,
|
||||
stepInterval: d,
|
||||
skipIfBlocked: true,
|
||||
destChan: ch,
|
||||
})
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
// Step moves the clock by Duration and notifies anyone that's called After,
|
||||
// Tick, or NewTimer.
|
||||
func (f *FakeClock) Step(d time.Duration) {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
f.setTimeLocked(f.time.Add(d))
|
||||
}
|
||||
|
||||
// SetTime sets the time.
|
||||
func (f *FakeClock) SetTime(t time.Time) {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
f.setTimeLocked(t)
|
||||
}
|
||||
|
||||
// Actually changes the time and checks any waiters. f must be write-locked.
|
||||
func (f *FakeClock) setTimeLocked(t time.Time) {
|
||||
f.time = t
|
||||
newWaiters := make([]*fakeClockWaiter, 0, len(f.waiters))
|
||||
for i := range f.waiters {
|
||||
w := f.waiters[i]
|
||||
if !w.targetTime.After(t) {
|
||||
|
||||
if w.skipIfBlocked {
|
||||
select {
|
||||
case w.destChan <- t:
|
||||
w.fired = true
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
w.destChan <- t
|
||||
w.fired = true
|
||||
}
|
||||
|
||||
if w.stepInterval > 0 {
|
||||
for !w.targetTime.After(t) {
|
||||
w.targetTime = w.targetTime.Add(w.stepInterval)
|
||||
}
|
||||
newWaiters = append(newWaiters, w)
|
||||
}
|
||||
|
||||
} else {
|
||||
newWaiters = append(newWaiters, f.waiters[i])
|
||||
}
|
||||
}
|
||||
f.waiters = newWaiters
|
||||
}
|
||||
|
||||
// HasWaiters returns true if After has been called on f but not yet satisfied (so you can
|
||||
// write race-free tests).
|
||||
func (f *FakeClock) HasWaiters() bool {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
return len(f.waiters) > 0
|
||||
}
|
||||
|
||||
// Sleep is akin to time.Sleep
|
||||
func (f *FakeClock) Sleep(d time.Duration) {
|
||||
f.Step(d)
|
||||
}
|
||||
|
||||
// IntervalClock implements clock.Clock, but each invocation of Now steps the clock forward the specified duration
|
||||
type IntervalClock struct {
|
||||
Time time.Time
|
||||
Duration time.Duration
|
||||
}
|
||||
|
||||
// Now returns i's time.
|
||||
func (i *IntervalClock) Now() time.Time {
|
||||
i.Time = i.Time.Add(i.Duration)
|
||||
return i.Time
|
||||
}
|
||||
|
||||
// Since returns time since the time in i.
|
||||
func (i *IntervalClock) Since(ts time.Time) time.Duration {
|
||||
return i.Time.Sub(ts)
|
||||
}
|
||||
|
||||
// After is unimplemented, will panic.
|
||||
// TODO: make interval clock use FakeClock so this can be implemented.
|
||||
func (*IntervalClock) After(d time.Duration) <-chan time.Time {
|
||||
panic("IntervalClock doesn't implement After")
|
||||
}
|
||||
|
||||
// NewTimer is unimplemented, will panic.
|
||||
// TODO: make interval clock use FakeClock so this can be implemented.
|
||||
func (*IntervalClock) NewTimer(d time.Duration) clock.Timer {
|
||||
panic("IntervalClock doesn't implement NewTimer")
|
||||
}
|
||||
|
||||
// Tick is unimplemented, will panic.
|
||||
// TODO: make interval clock use FakeClock so this can be implemented.
|
||||
func (*IntervalClock) Tick(d time.Duration) <-chan time.Time {
|
||||
panic("IntervalClock doesn't implement Tick")
|
||||
}
|
||||
|
||||
// Sleep is unimplemented, will panic.
|
||||
func (*IntervalClock) Sleep(d time.Duration) {
|
||||
panic("IntervalClock doesn't implement Sleep")
|
||||
}
|
||||
|
||||
var _ = clock.Timer(&fakeTimer{})
|
||||
|
||||
// fakeTimer implements clock.Timer based on a FakeClock.
|
||||
type fakeTimer struct {
|
||||
fakeClock *FakeClock
|
||||
waiter fakeClockWaiter
|
||||
}
|
||||
|
||||
// C returns the channel that notifies when this timer has fired.
|
||||
func (f *fakeTimer) C() <-chan time.Time {
|
||||
return f.waiter.destChan
|
||||
}
|
||||
|
||||
// Stop stops the timer and returns true if the timer has not yet fired, or false otherwise.
|
||||
func (f *fakeTimer) Stop() bool {
|
||||
f.fakeClock.lock.Lock()
|
||||
defer f.fakeClock.lock.Unlock()
|
||||
|
||||
newWaiters := make([]*fakeClockWaiter, 0, len(f.fakeClock.waiters))
|
||||
for i := range f.fakeClock.waiters {
|
||||
w := f.fakeClock.waiters[i]
|
||||
if w != &f.waiter {
|
||||
newWaiters = append(newWaiters, w)
|
||||
}
|
||||
}
|
||||
|
||||
f.fakeClock.waiters = newWaiters
|
||||
|
||||
return !f.waiter.fired
|
||||
}
|
||||
|
||||
// Reset resets the timer to the fake clock's "now" + d. It returns true if the timer has not yet
|
||||
// fired, or false otherwise.
|
||||
func (f *fakeTimer) Reset(d time.Duration) bool {
|
||||
f.fakeClock.lock.Lock()
|
||||
defer f.fakeClock.lock.Unlock()
|
||||
|
||||
active := !f.waiter.fired
|
||||
|
||||
f.waiter.fired = false
|
||||
f.waiter.targetTime = f.fakeClock.time.Add(d)
|
||||
|
||||
var isWaiting bool
|
||||
for i := range f.fakeClock.waiters {
|
||||
w := f.fakeClock.waiters[i]
|
||||
if w == &f.waiter {
|
||||
isWaiting = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isWaiting {
|
||||
f.fakeClock.waiters = append(f.fakeClock.waiters, &f.waiter)
|
||||
}
|
||||
|
||||
return active
|
||||
}
|
||||
274
vendor/k8s.io/utils/clock/testing/fake_clock_test.go
generated
vendored
274
vendor/k8s.io/utils/clock/testing/fake_clock_test.go
generated
vendored
@@ -1,274 +0,0 @@
|
||||
/*
|
||||
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 testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestFakeClock(t *testing.T) {
|
||||
startTime := time.Now()
|
||||
tc := NewFakeClock(startTime)
|
||||
tc.Step(time.Second)
|
||||
now := tc.Now()
|
||||
if now.Sub(startTime) != time.Second {
|
||||
t.Errorf("input: %s now=%s gap=%s expected=%s", startTime, now, now.Sub(startTime), time.Second)
|
||||
}
|
||||
|
||||
tt := tc.Now()
|
||||
tc.SetTime(tt.Add(time.Hour))
|
||||
if tc.Now().Sub(tt) != time.Hour {
|
||||
t.Errorf("input: %s now=%s gap=%s expected=%s", tt, tc.Now(), tc.Now().Sub(tt), time.Hour)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFakeClockSleep(t *testing.T) {
|
||||
startTime := time.Now()
|
||||
tc := NewFakeClock(startTime)
|
||||
tc.Sleep(time.Duration(1) * time.Hour)
|
||||
now := tc.Now()
|
||||
if now.Sub(startTime) != time.Hour {
|
||||
t.Errorf("Fake sleep failed, expected time to advance by one hour, instead, its %v", now.Sub(startTime))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFakeAfter(t *testing.T) {
|
||||
tc := NewFakeClock(time.Now())
|
||||
if tc.HasWaiters() {
|
||||
t.Errorf("unexpected waiter?")
|
||||
}
|
||||
oneSec := tc.After(time.Second)
|
||||
if !tc.HasWaiters() {
|
||||
t.Errorf("unexpected lack of waiter?")
|
||||
}
|
||||
|
||||
oneOhOneSec := tc.After(time.Second + time.Millisecond)
|
||||
twoSec := tc.After(2 * time.Second)
|
||||
select {
|
||||
case <-oneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-oneOhOneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
}
|
||||
|
||||
tc.Step(999 * time.Millisecond)
|
||||
select {
|
||||
case <-oneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-oneOhOneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
}
|
||||
|
||||
tc.Step(time.Millisecond)
|
||||
select {
|
||||
case <-oneSec:
|
||||
// Expected!
|
||||
case <-oneOhOneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
t.Errorf("unexpected non-channel read")
|
||||
}
|
||||
tc.Step(time.Millisecond)
|
||||
select {
|
||||
case <-oneSec:
|
||||
// should not double-trigger!
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-oneOhOneSec:
|
||||
// Expected!
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
t.Errorf("unexpected non-channel read")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFakeTick(t *testing.T) {
|
||||
tc := NewFakeClock(time.Now())
|
||||
if tc.HasWaiters() {
|
||||
t.Errorf("unexpected waiter?")
|
||||
}
|
||||
oneSec := tc.Tick(time.Second)
|
||||
if !tc.HasWaiters() {
|
||||
t.Errorf("unexpected lack of waiter?")
|
||||
}
|
||||
|
||||
oneOhOneSec := tc.Tick(time.Second + time.Millisecond)
|
||||
twoSec := tc.Tick(2 * time.Second)
|
||||
select {
|
||||
case <-oneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-oneOhOneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
}
|
||||
|
||||
tc.Step(999 * time.Millisecond) // t=.999
|
||||
select {
|
||||
case <-oneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-oneOhOneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
}
|
||||
|
||||
tc.Step(time.Millisecond) // t=1.000
|
||||
select {
|
||||
case <-oneSec:
|
||||
// Expected!
|
||||
case <-oneOhOneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
t.Errorf("unexpected non-channel read")
|
||||
}
|
||||
tc.Step(time.Millisecond) // t=1.001
|
||||
select {
|
||||
case <-oneSec:
|
||||
// should not double-trigger!
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-oneOhOneSec:
|
||||
// Expected!
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
t.Errorf("unexpected non-channel read")
|
||||
}
|
||||
|
||||
tc.Step(time.Second) // t=2.001
|
||||
tc.Step(time.Second) // t=3.001
|
||||
tc.Step(time.Second) // t=4.001
|
||||
tc.Step(time.Second) // t=5.001
|
||||
|
||||
// The one second ticker should not accumulate ticks
|
||||
accumulatedTicks := 0
|
||||
drained := false
|
||||
for !drained {
|
||||
select {
|
||||
case <-oneSec:
|
||||
accumulatedTicks++
|
||||
default:
|
||||
drained = true
|
||||
}
|
||||
}
|
||||
if accumulatedTicks != 1 {
|
||||
t.Errorf("unexpected number of accumulated ticks: %d", accumulatedTicks)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFakeStop(t *testing.T) {
|
||||
tc := NewFakeClock(time.Now())
|
||||
timer := tc.NewTimer(time.Second)
|
||||
if !tc.HasWaiters() {
|
||||
t.Errorf("expected a waiter to be present, but it is not")
|
||||
}
|
||||
timer.Stop()
|
||||
if tc.HasWaiters() {
|
||||
t.Errorf("expected existing waiter to be cleaned up, but it is still present")
|
||||
}
|
||||
}
|
||||
|
||||
// This tests the pattern documented in the go docs here: https://golang.org/pkg/time/#Timer.Stop
|
||||
// This pattern is required to safely reset a timer, so should be common.
|
||||
// This also tests resetting the timer
|
||||
func TestFakeStopDrain(t *testing.T) {
|
||||
start := time.Time{}
|
||||
tc := NewFakeClock(start)
|
||||
timer := tc.NewTimer(time.Second)
|
||||
tc.Step(1 * time.Second)
|
||||
// Effectively `if !timer.Stop { <-t.C }` but with more asserts
|
||||
if timer.Stop() {
|
||||
t.Errorf("stop should report the timer had triggered")
|
||||
}
|
||||
if readTime := assertReadTime(t, timer.C()); !readTime.Equal(start.Add(1 * time.Second)) {
|
||||
t.Errorf("timer should have ticked after 1 second, got %v", readTime)
|
||||
}
|
||||
|
||||
timer.Reset(time.Second)
|
||||
if !tc.HasWaiters() {
|
||||
t.Errorf("expected a waiter to be present, but it is not")
|
||||
}
|
||||
select {
|
||||
case <-timer.C():
|
||||
t.Fatal("got time early on clock; haven't stepped yet")
|
||||
default:
|
||||
}
|
||||
tc.Step(1 * time.Second)
|
||||
if readTime := assertReadTime(t, timer.C()); !readTime.Equal(start.Add(2 * time.Second)) {
|
||||
t.Errorf("timer should have ticked again after reset + 1 more second, got %v", readTime)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimerNegative(t *testing.T) {
|
||||
tc := NewFakeClock(time.Now())
|
||||
timer := tc.NewTimer(-1 * time.Second)
|
||||
if !tc.HasWaiters() {
|
||||
t.Errorf("expected a waiter to be present, but it is not")
|
||||
}
|
||||
// force waiters to be called
|
||||
tc.Step(0)
|
||||
tick := assertReadTime(t, timer.C())
|
||||
if tick != tc.Now() {
|
||||
t.Errorf("expected -1s to turn into now: %v != %v", tick, tc.Now())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTickNegative(t *testing.T) {
|
||||
// The stdlib 'Tick' returns nil for negative and zero values, so our fake
|
||||
// should too.
|
||||
tc := NewFakeClock(time.Now())
|
||||
if tick := tc.Tick(-1 * time.Second); tick != nil {
|
||||
t.Errorf("expected negative tick to be nil: %v", tick)
|
||||
}
|
||||
if tick := tc.Tick(0); tick != nil {
|
||||
t.Errorf("expected negative tick to be nil: %v", tick)
|
||||
}
|
||||
}
|
||||
|
||||
// assertReadTime asserts that the channel can be read and returns the time it
|
||||
// reads from the channel.
|
||||
func assertReadTime(t testing.TB, c <-chan time.Time) time.Time {
|
||||
type helper interface {
|
||||
Helper()
|
||||
}
|
||||
if h, ok := t.(helper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
select {
|
||||
case ti, ok := <-c:
|
||||
if !ok {
|
||||
t.Fatalf("expected to read time from channel, but it was closed")
|
||||
}
|
||||
return ti
|
||||
default:
|
||||
t.Fatalf("expected to read time from channel, but couldn't")
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
3
vendor/k8s.io/utils/code-of-conduct.md
generated
vendored
3
vendor/k8s.io/utils/code-of-conduct.md
generated
vendored
@@ -1,3 +0,0 @@
|
||||
# Kubernetes Community Code of Conduct
|
||||
|
||||
Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)
|
||||
314
vendor/k8s.io/utils/diff/diff.go
generated
vendored
314
vendor/k8s.io/utils/diff/diff.go
generated
vendored
@@ -1,314 +0,0 @@
|
||||
/*
|
||||
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 diff
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
|
||||
"k8s.io/utils/field"
|
||||
)
|
||||
|
||||
// StringDiff diffs a and b and returns a human readable diff.
|
||||
func StringDiff(a, b string) string {
|
||||
ba := []byte(a)
|
||||
bb := []byte(b)
|
||||
out := []byte{}
|
||||
i := 0
|
||||
for ; i < len(ba) && i < len(bb); i++ {
|
||||
if ba[i] != bb[i] {
|
||||
break
|
||||
}
|
||||
out = append(out, ba[i])
|
||||
}
|
||||
out = append(out, []byte("\n\nA: ")...)
|
||||
out = append(out, ba[i:]...)
|
||||
out = append(out, []byte("\n\nB: ")...)
|
||||
out = append(out, bb[i:]...)
|
||||
out = append(out, []byte("\n\n")...)
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// ObjectDiff writes the two objects out as JSON and prints out the identical part of
|
||||
// the objects followed by the remaining part of 'a' and finally the remaining part of 'b'.
|
||||
// For debugging tests.
|
||||
func ObjectDiff(a, b interface{}) string {
|
||||
ab, err := json.Marshal(a)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("a: %v", err))
|
||||
}
|
||||
bb, err := json.Marshal(b)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("b: %v", err))
|
||||
}
|
||||
return StringDiff(string(ab), string(bb))
|
||||
}
|
||||
|
||||
// ObjectGoPrintDiff is like ObjectDiff, but uses go-spew to print the objects,
|
||||
// which shows absolutely everything by recursing into every single pointer
|
||||
// (go's %#v formatters OTOH stop at a certain point). This is needed when you
|
||||
// can't figure out why reflect.DeepEqual is returning false and nothing is
|
||||
// showing you differences. This will.
|
||||
func ObjectGoPrintDiff(a, b interface{}) string {
|
||||
s := spew.ConfigState{DisableMethods: true}
|
||||
return StringDiff(
|
||||
s.Sprintf("%#v", a),
|
||||
s.Sprintf("%#v", b),
|
||||
)
|
||||
}
|
||||
|
||||
// ObjectReflectDiff returns a diff computed through reflection, without serializing to JSON.
|
||||
func ObjectReflectDiff(a, b interface{}) string {
|
||||
vA, vB := reflect.ValueOf(a), reflect.ValueOf(b)
|
||||
if vA.Type() != vB.Type() {
|
||||
return fmt.Sprintf("type A %T and type B %T do not match", a, b)
|
||||
}
|
||||
diffs := objectReflectDiff(field.NewPath("object"), vA, vB)
|
||||
if len(diffs) == 0 {
|
||||
return "<no diffs>"
|
||||
}
|
||||
out := []string{""}
|
||||
for _, d := range diffs {
|
||||
elidedA, elidedB := limit(d.a, d.b, 80)
|
||||
out = append(out,
|
||||
fmt.Sprintf("%s:", d.path),
|
||||
fmt.Sprintf(" a: %s", elidedA),
|
||||
fmt.Sprintf(" b: %s", elidedB),
|
||||
)
|
||||
}
|
||||
return strings.Join(out, "\n")
|
||||
}
|
||||
|
||||
// limit:
|
||||
// 1. stringifies aObj and bObj
|
||||
// 2. elides identical prefixes if either is too long
|
||||
// 3. elides remaining content from the end if either is too long
|
||||
func limit(aObj, bObj interface{}, max int) (string, string) {
|
||||
elidedPrefix := ""
|
||||
elidedASuffix := ""
|
||||
elidedBSuffix := ""
|
||||
a, b := fmt.Sprintf("%#v", aObj), fmt.Sprintf("%#v", bObj)
|
||||
|
||||
if aObj != nil && bObj != nil {
|
||||
if aType, bType := fmt.Sprintf("%T", aObj), fmt.Sprintf("%T", bObj); aType != bType {
|
||||
a = fmt.Sprintf("%s (%s)", a, aType)
|
||||
b = fmt.Sprintf("%s (%s)", b, bType)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
switch {
|
||||
case len(a) > max && len(a) > 4 && len(b) > 4 && a[:4] == b[:4]:
|
||||
// a is too long, b has data, and the first several characters are the same
|
||||
elidedPrefix = "..."
|
||||
a = a[2:]
|
||||
b = b[2:]
|
||||
|
||||
case len(b) > max && len(b) > 4 && len(a) > 4 && a[:4] == b[:4]:
|
||||
// b is too long, a has data, and the first several characters are the same
|
||||
elidedPrefix = "..."
|
||||
a = a[2:]
|
||||
b = b[2:]
|
||||
|
||||
case len(a) > max:
|
||||
a = a[:max]
|
||||
elidedASuffix = "..."
|
||||
|
||||
case len(b) > max:
|
||||
b = b[:max]
|
||||
elidedBSuffix = "..."
|
||||
|
||||
default:
|
||||
// both are short enough
|
||||
return elidedPrefix + a + elidedASuffix, elidedPrefix + b + elidedBSuffix
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func public(s string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
return s[:1] == strings.ToUpper(s[:1])
|
||||
}
|
||||
|
||||
type diff struct {
|
||||
path *field.Path
|
||||
a, b interface{}
|
||||
}
|
||||
|
||||
type orderedDiffs []diff
|
||||
|
||||
func (d orderedDiffs) Len() int { return len(d) }
|
||||
func (d orderedDiffs) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||
func (d orderedDiffs) Less(i, j int) bool {
|
||||
a, b := d[i].path.String(), d[j].path.String()
|
||||
if a < b {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func objectReflectDiff(path *field.Path, a, b reflect.Value) []diff {
|
||||
switch a.Type().Kind() {
|
||||
case reflect.Struct:
|
||||
var changes []diff
|
||||
for i := 0; i < a.Type().NumField(); i++ {
|
||||
if !public(a.Type().Field(i).Name) {
|
||||
if reflect.DeepEqual(a.Interface(), b.Interface()) {
|
||||
continue
|
||||
}
|
||||
return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
|
||||
}
|
||||
if sub := objectReflectDiff(path.Child(a.Type().Field(i).Name), a.Field(i), b.Field(i)); len(sub) > 0 {
|
||||
changes = append(changes, sub...)
|
||||
}
|
||||
}
|
||||
return changes
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
if a.IsNil() || b.IsNil() {
|
||||
switch {
|
||||
case a.IsNil() && b.IsNil():
|
||||
return nil
|
||||
case a.IsNil():
|
||||
return []diff{{path: path, a: nil, b: b.Interface()}}
|
||||
default:
|
||||
return []diff{{path: path, a: a.Interface(), b: nil}}
|
||||
}
|
||||
}
|
||||
return objectReflectDiff(path, a.Elem(), b.Elem())
|
||||
case reflect.Chan:
|
||||
if !reflect.DeepEqual(a.Interface(), b.Interface()) {
|
||||
return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
|
||||
}
|
||||
return nil
|
||||
case reflect.Slice:
|
||||
lA, lB := a.Len(), b.Len()
|
||||
l := lA
|
||||
if lB < lA {
|
||||
l = lB
|
||||
}
|
||||
if lA == lB && lA == 0 {
|
||||
if a.IsNil() != b.IsNil() {
|
||||
return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var diffs []diff
|
||||
for i := 0; i < l; i++ {
|
||||
if !reflect.DeepEqual(a.Index(i), b.Index(i)) {
|
||||
diffs = append(diffs, objectReflectDiff(path.Index(i), a.Index(i), b.Index(i))...)
|
||||
}
|
||||
}
|
||||
for i := l; i < lA; i++ {
|
||||
diffs = append(diffs, diff{path: path.Index(i), a: a.Index(i), b: nil})
|
||||
}
|
||||
for i := l; i < lB; i++ {
|
||||
diffs = append(diffs, diff{path: path.Index(i), a: nil, b: b.Index(i)})
|
||||
}
|
||||
return diffs
|
||||
case reflect.Map:
|
||||
if reflect.DeepEqual(a.Interface(), b.Interface()) {
|
||||
return nil
|
||||
}
|
||||
aKeys := make(map[interface{}]interface{})
|
||||
for _, key := range a.MapKeys() {
|
||||
aKeys[key.Interface()] = a.MapIndex(key).Interface()
|
||||
}
|
||||
var missing []diff
|
||||
for _, key := range b.MapKeys() {
|
||||
if _, ok := aKeys[key.Interface()]; ok {
|
||||
delete(aKeys, key.Interface())
|
||||
if reflect.DeepEqual(a.MapIndex(key).Interface(), b.MapIndex(key).Interface()) {
|
||||
continue
|
||||
}
|
||||
missing = append(missing, objectReflectDiff(path.Key(fmt.Sprintf("%s", key.Interface())), a.MapIndex(key), b.MapIndex(key))...)
|
||||
continue
|
||||
}
|
||||
missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key.Interface())), a: nil, b: b.MapIndex(key).Interface()})
|
||||
}
|
||||
for key, value := range aKeys {
|
||||
missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key)), a: value, b: nil})
|
||||
}
|
||||
if len(missing) == 0 {
|
||||
missing = append(missing, diff{path: path, a: a.Interface(), b: b.Interface()})
|
||||
}
|
||||
sort.Sort(orderedDiffs(missing))
|
||||
return missing
|
||||
default:
|
||||
if reflect.DeepEqual(a.Interface(), b.Interface()) {
|
||||
return nil
|
||||
}
|
||||
if !a.CanInterface() {
|
||||
return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
|
||||
}
|
||||
return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
|
||||
}
|
||||
}
|
||||
|
||||
// ObjectGoPrintSideBySide prints a and b as textual dumps side by side,
|
||||
// enabling easy visual scanning for mismatches.
|
||||
func ObjectGoPrintSideBySide(a, b interface{}) string {
|
||||
s := spew.ConfigState{
|
||||
Indent: " ",
|
||||
// Extra deep spew.
|
||||
DisableMethods: true,
|
||||
}
|
||||
sA := s.Sdump(a)
|
||||
sB := s.Sdump(b)
|
||||
|
||||
linesA := strings.Split(sA, "\n")
|
||||
linesB := strings.Split(sB, "\n")
|
||||
width := 0
|
||||
for _, s := range linesA {
|
||||
l := len(s)
|
||||
if l > width {
|
||||
width = l
|
||||
}
|
||||
}
|
||||
for _, s := range linesB {
|
||||
l := len(s)
|
||||
if l > width {
|
||||
width = l
|
||||
}
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
w := tabwriter.NewWriter(buf, width, 0, 1, ' ', 0)
|
||||
max := len(linesA)
|
||||
if len(linesB) > max {
|
||||
max = len(linesB)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
var a, b string
|
||||
if i < len(linesA) {
|
||||
a = linesA[i]
|
||||
}
|
||||
if i < len(linesB) {
|
||||
b = linesB[i]
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\n", a, b)
|
||||
}
|
||||
w.Flush()
|
||||
return buf.String()
|
||||
}
|
||||
148
vendor/k8s.io/utils/diff/diff_test.go
generated
vendored
148
vendor/k8s.io/utils/diff/diff_test.go
generated
vendored
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
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 diff
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestObjectReflectDiff(t *testing.T) {
|
||||
type struct1 struct{ A []int }
|
||||
|
||||
testCases := map[string]struct {
|
||||
a, b interface{}
|
||||
out string
|
||||
}{
|
||||
"map": {
|
||||
a: map[string]int{},
|
||||
b: map[string]int{},
|
||||
},
|
||||
"detect nil map": {
|
||||
a: map[string]int(nil),
|
||||
b: map[string]int{},
|
||||
out: `
|
||||
object:
|
||||
a: map[string]int(nil)
|
||||
b: map[string]int{}`,
|
||||
},
|
||||
"detect map changes": {
|
||||
a: map[string]int{"test": 1, "other": 2},
|
||||
b: map[string]int{"test": 2, "third": 3},
|
||||
out: `
|
||||
object[other]:
|
||||
a: 2
|
||||
b: <nil>
|
||||
object[test]:
|
||||
a: 1
|
||||
b: 2
|
||||
object[third]:
|
||||
a: <nil>
|
||||
b: 3`,
|
||||
},
|
||||
"nil slice": {a: struct1{A: nil}, b: struct1{A: nil}},
|
||||
"empty slice": {a: struct1{A: []int{}}, b: struct1{A: []int{}}},
|
||||
"detect slice changes 1": {a: struct1{A: []int{1}}, b: struct1{A: []int{2}}, out: `
|
||||
object.A[0]:
|
||||
a: 1
|
||||
b: 2`,
|
||||
},
|
||||
"detect slice changes 2": {a: struct1{A: []int{}}, b: struct1{A: []int{2}}, out: `
|
||||
object.A[0]:
|
||||
a: <nil>
|
||||
b: 2`,
|
||||
},
|
||||
"detect slice changes 3": {a: struct1{A: []int{1}}, b: struct1{A: []int{}}, out: `
|
||||
object.A[0]:
|
||||
a: 1
|
||||
b: <nil>`,
|
||||
},
|
||||
"detect nil vs empty slices": {a: struct1{A: nil}, b: struct1{A: []int{}}, out: `
|
||||
object.A:
|
||||
a: []int(nil)
|
||||
b: []int{}`,
|
||||
},
|
||||
"display type differences": {a: []interface{}{int64(1)}, b: []interface{}{uint64(1)}, out: `
|
||||
object[0]:
|
||||
a: 1 (int64)
|
||||
b: 0x1 (uint64)`,
|
||||
},
|
||||
}
|
||||
for name, test := range testCases {
|
||||
expect := test.out
|
||||
if len(expect) == 0 {
|
||||
expect = "<no diffs>"
|
||||
}
|
||||
if actual := ObjectReflectDiff(test.a, test.b); actual != expect {
|
||||
t.Errorf("%s: unexpected output: %s", name, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringDiff(t *testing.T) {
|
||||
diff := StringDiff("aaabb", "aaacc")
|
||||
expect := "aaa\n\nA: bb\n\nB: cc\n\n"
|
||||
if diff != expect {
|
||||
t.Errorf("diff returned %v", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLimit(t *testing.T) {
|
||||
testcases := []struct {
|
||||
a interface{}
|
||||
b interface{}
|
||||
expectA string
|
||||
expectB string
|
||||
}{
|
||||
{
|
||||
a: `short a`,
|
||||
b: `short b`,
|
||||
expectA: `"short a"`,
|
||||
expectB: `"short b"`,
|
||||
},
|
||||
{
|
||||
a: `short a`,
|
||||
b: `long b needs truncating`,
|
||||
expectA: `"short a"`,
|
||||
expectB: `"long b ne...`,
|
||||
},
|
||||
{
|
||||
a: `long a needs truncating`,
|
||||
b: `long b needs truncating`,
|
||||
expectA: `...g a needs ...`,
|
||||
expectB: `...g b needs ...`,
|
||||
},
|
||||
{
|
||||
a: `long common prefix with different stuff at the end of a`,
|
||||
b: `long common prefix with different stuff at the end of b`,
|
||||
expectA: `...end of a"`,
|
||||
expectB: `...end of b"`,
|
||||
},
|
||||
{
|
||||
a: `long common prefix with different stuff at the end of a`,
|
||||
b: `long common prefix with different stuff at the end of b which continues`,
|
||||
expectA: `...of a"`,
|
||||
expectB: `...of b which...`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
a, b := limit(tc.a, tc.b, 10)
|
||||
if a != tc.expectA || b != tc.expectB {
|
||||
t.Errorf("limit(%q, %q)\n\texpected: %s, %s\n\tgot: %s, %s", tc.a, tc.b, tc.expectA, tc.expectB, a, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
18
vendor/k8s.io/utils/exec/doc.go
generated
vendored
18
vendor/k8s.io/utils/exec/doc.go
generated
vendored
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
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 exec provides an injectable interface and implementations for running commands.
|
||||
package exec // import "k8s.io/utils/exec"
|
||||
252
vendor/k8s.io/utils/exec/exec.go
generated
vendored
252
vendor/k8s.io/utils/exec/exec.go
generated
vendored
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
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 exec
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
osexec "os/exec"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrExecutableNotFound is returned if the executable is not found.
|
||||
var ErrExecutableNotFound = osexec.ErrNotFound
|
||||
|
||||
// Interface is an interface that presents a subset of the os/exec API. Use this
|
||||
// when you want to inject fakeable/mockable exec behavior.
|
||||
type Interface interface {
|
||||
// Command returns a Cmd instance which can be used to run a single command.
|
||||
// This follows the pattern of package os/exec.
|
||||
Command(cmd string, args ...string) Cmd
|
||||
|
||||
// CommandContext returns a Cmd instance which can be used to run a single command.
|
||||
//
|
||||
// The provided context is used to kill the process if the context becomes done
|
||||
// before the command completes on its own. For example, a timeout can be set in
|
||||
// the context.
|
||||
CommandContext(ctx context.Context, cmd string, args ...string) Cmd
|
||||
|
||||
// LookPath wraps os/exec.LookPath
|
||||
LookPath(file string) (string, error)
|
||||
}
|
||||
|
||||
// Cmd is an interface that presents an API that is very similar to Cmd from os/exec.
|
||||
// As more functionality is needed, this can grow. Since Cmd is a struct, we will have
|
||||
// to replace fields with get/set method pairs.
|
||||
type Cmd interface {
|
||||
// Run runs the command to the completion.
|
||||
Run() error
|
||||
// CombinedOutput runs the command and returns its combined standard output
|
||||
// and standard error. This follows the pattern of package os/exec.
|
||||
CombinedOutput() ([]byte, error)
|
||||
// Output runs the command and returns standard output, but not standard err
|
||||
Output() ([]byte, error)
|
||||
SetDir(dir string)
|
||||
SetStdin(in io.Reader)
|
||||
SetStdout(out io.Writer)
|
||||
SetStderr(out io.Writer)
|
||||
SetEnv(env []string)
|
||||
|
||||
// StdoutPipe and StderrPipe for getting the process' Stdout and Stderr as
|
||||
// Readers
|
||||
StdoutPipe() (io.ReadCloser, error)
|
||||
StderrPipe() (io.ReadCloser, error)
|
||||
|
||||
// Start and Wait are for running a process non-blocking
|
||||
Start() error
|
||||
Wait() error
|
||||
|
||||
// Stops the command by sending SIGTERM. It is not guaranteed the
|
||||
// process will stop before this function returns. If the process is not
|
||||
// responding, an internal timer function will send a SIGKILL to force
|
||||
// terminate after 10 seconds.
|
||||
Stop()
|
||||
}
|
||||
|
||||
// ExitError is an interface that presents an API similar to os.ProcessState, which is
|
||||
// what ExitError from os/exec is. This is designed to make testing a bit easier and
|
||||
// probably loses some of the cross-platform properties of the underlying library.
|
||||
type ExitError interface {
|
||||
String() string
|
||||
Error() string
|
||||
Exited() bool
|
||||
ExitStatus() int
|
||||
}
|
||||
|
||||
// Implements Interface in terms of really exec()ing.
|
||||
type executor struct{}
|
||||
|
||||
// New returns a new Interface which will os/exec to run commands.
|
||||
func New() Interface {
|
||||
return &executor{}
|
||||
}
|
||||
|
||||
// Command is part of the Interface interface.
|
||||
func (executor *executor) Command(cmd string, args ...string) Cmd {
|
||||
return (*cmdWrapper)(osexec.Command(cmd, args...))
|
||||
}
|
||||
|
||||
// CommandContext is part of the Interface interface.
|
||||
func (executor *executor) CommandContext(ctx context.Context, cmd string, args ...string) Cmd {
|
||||
return (*cmdWrapper)(osexec.CommandContext(ctx, cmd, args...))
|
||||
}
|
||||
|
||||
// LookPath is part of the Interface interface
|
||||
func (executor *executor) LookPath(file string) (string, error) {
|
||||
return osexec.LookPath(file)
|
||||
}
|
||||
|
||||
// Wraps exec.Cmd so we can capture errors.
|
||||
type cmdWrapper osexec.Cmd
|
||||
|
||||
var _ Cmd = &cmdWrapper{}
|
||||
|
||||
func (cmd *cmdWrapper) SetDir(dir string) {
|
||||
cmd.Dir = dir
|
||||
}
|
||||
|
||||
func (cmd *cmdWrapper) SetStdin(in io.Reader) {
|
||||
cmd.Stdin = in
|
||||
}
|
||||
|
||||
func (cmd *cmdWrapper) SetStdout(out io.Writer) {
|
||||
cmd.Stdout = out
|
||||
}
|
||||
|
||||
func (cmd *cmdWrapper) SetStderr(out io.Writer) {
|
||||
cmd.Stderr = out
|
||||
}
|
||||
|
||||
func (cmd *cmdWrapper) SetEnv(env []string) {
|
||||
cmd.Env = env
|
||||
}
|
||||
|
||||
func (cmd *cmdWrapper) StdoutPipe() (io.ReadCloser, error) {
|
||||
r, err := (*osexec.Cmd)(cmd).StdoutPipe()
|
||||
return r, handleError(err)
|
||||
}
|
||||
|
||||
func (cmd *cmdWrapper) StderrPipe() (io.ReadCloser, error) {
|
||||
r, err := (*osexec.Cmd)(cmd).StderrPipe()
|
||||
return r, handleError(err)
|
||||
}
|
||||
|
||||
func (cmd *cmdWrapper) Start() error {
|
||||
err := (*osexec.Cmd)(cmd).Start()
|
||||
return handleError(err)
|
||||
}
|
||||
|
||||
func (cmd *cmdWrapper) Wait() error {
|
||||
err := (*osexec.Cmd)(cmd).Wait()
|
||||
return handleError(err)
|
||||
}
|
||||
|
||||
// Run is part of the Cmd interface.
|
||||
func (cmd *cmdWrapper) Run() error {
|
||||
err := (*osexec.Cmd)(cmd).Run()
|
||||
return handleError(err)
|
||||
}
|
||||
|
||||
// CombinedOutput is part of the Cmd interface.
|
||||
func (cmd *cmdWrapper) CombinedOutput() ([]byte, error) {
|
||||
out, err := (*osexec.Cmd)(cmd).CombinedOutput()
|
||||
return out, handleError(err)
|
||||
}
|
||||
|
||||
func (cmd *cmdWrapper) Output() ([]byte, error) {
|
||||
out, err := (*osexec.Cmd)(cmd).Output()
|
||||
return out, handleError(err)
|
||||
}
|
||||
|
||||
// Stop is part of the Cmd interface.
|
||||
func (cmd *cmdWrapper) Stop() {
|
||||
c := (*osexec.Cmd)(cmd)
|
||||
|
||||
if c.Process == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.Process.Signal(syscall.SIGTERM)
|
||||
|
||||
time.AfterFunc(10*time.Second, func() {
|
||||
if !c.ProcessState.Exited() {
|
||||
c.Process.Signal(syscall.SIGKILL)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func handleError(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch e := err.(type) {
|
||||
case *osexec.ExitError:
|
||||
return &ExitErrorWrapper{e}
|
||||
case *osexec.Error:
|
||||
if e.Err == osexec.ErrNotFound {
|
||||
return ErrExecutableNotFound
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ExitErrorWrapper is an implementation of ExitError in terms of os/exec ExitError.
|
||||
// Note: standard exec.ExitError is type *os.ProcessState, which already implements Exited().
|
||||
type ExitErrorWrapper struct {
|
||||
*osexec.ExitError
|
||||
}
|
||||
|
||||
var _ ExitError = &ExitErrorWrapper{}
|
||||
|
||||
// ExitStatus is part of the ExitError interface.
|
||||
func (eew ExitErrorWrapper) ExitStatus() int {
|
||||
ws, ok := eew.Sys().(syscall.WaitStatus)
|
||||
if !ok {
|
||||
panic("can't call ExitStatus() on a non-WaitStatus exitErrorWrapper")
|
||||
}
|
||||
return ws.ExitStatus()
|
||||
}
|
||||
|
||||
// CodeExitError is an implementation of ExitError consisting of an error object
|
||||
// and an exit code (the upper bits of os.exec.ExitStatus).
|
||||
type CodeExitError struct {
|
||||
Err error
|
||||
Code int
|
||||
}
|
||||
|
||||
var _ ExitError = CodeExitError{}
|
||||
|
||||
func (e CodeExitError) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
func (e CodeExitError) String() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
// Exited is to check if the process has finished
|
||||
func (e CodeExitError) Exited() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// ExitStatus is for checking the error code
|
||||
func (e CodeExitError) ExitStatus() int {
|
||||
return e.Code
|
||||
}
|
||||
215
vendor/k8s.io/utils/exec/exec_test.go
generated
vendored
215
vendor/k8s.io/utils/exec/exec_test.go
generated
vendored
@@ -1,215 +0,0 @@
|
||||
/*
|
||||
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 exec
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
osexec "os/exec"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestExecutorNoArgs(t *testing.T) {
|
||||
ex := New()
|
||||
|
||||
cmd := ex.Command("true")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if len(out) != 0 {
|
||||
t.Errorf("expected no output, got %q", string(out))
|
||||
}
|
||||
|
||||
cmd = ex.Command("false")
|
||||
out, err = cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
t.Errorf("expected failure, got nil error")
|
||||
}
|
||||
if len(out) != 0 {
|
||||
t.Errorf("expected no output, got %q", string(out))
|
||||
}
|
||||
ee, ok := err.(ExitError)
|
||||
if !ok {
|
||||
t.Errorf("expected an ExitError, got %+v", err)
|
||||
}
|
||||
if ee.Exited() {
|
||||
if code := ee.ExitStatus(); code != 1 {
|
||||
t.Errorf("expected exit status 1, got %d", code)
|
||||
}
|
||||
}
|
||||
|
||||
cmd = ex.Command("/does/not/exist")
|
||||
_, err = cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
t.Errorf("expected failure, got nil error")
|
||||
}
|
||||
if ee, ok := err.(ExitError); ok {
|
||||
t.Errorf("expected non-ExitError, got %+v", ee)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecutorWithArgs(t *testing.T) {
|
||||
ex := New()
|
||||
|
||||
cmd := ex.Command("echo", "stdout")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %+v", err)
|
||||
}
|
||||
if string(out) != "stdout\n" {
|
||||
t.Errorf("unexpected output: %q", string(out))
|
||||
}
|
||||
|
||||
cmd = ex.Command("/bin/sh", "-c", "echo stderr > /dev/stderr")
|
||||
out, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %+v", err)
|
||||
}
|
||||
if string(out) != "stderr\n" {
|
||||
t.Errorf("unexpected output: %q", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookPath(t *testing.T) {
|
||||
ex := New()
|
||||
|
||||
shExpected, _ := osexec.LookPath("sh")
|
||||
sh, _ := ex.LookPath("sh")
|
||||
if sh != shExpected {
|
||||
t.Errorf("unexpected result for LookPath: got %s, expected %s", sh, shExpected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecutableNotFound(t *testing.T) {
|
||||
exec := New()
|
||||
|
||||
cmd := exec.Command("fake_executable_name")
|
||||
_, err := cmd.CombinedOutput()
|
||||
if err != ErrExecutableNotFound {
|
||||
t.Errorf("cmd.CombinedOutput(): Expected error ErrExecutableNotFound but got %v", err)
|
||||
}
|
||||
|
||||
cmd = exec.Command("fake_executable_name")
|
||||
_, err = cmd.Output()
|
||||
if err != ErrExecutableNotFound {
|
||||
t.Errorf("cmd.Output(): Expected error ErrExecutableNotFound but got %v", err)
|
||||
}
|
||||
|
||||
cmd = exec.Command("fake_executable_name")
|
||||
err = cmd.Run()
|
||||
if err != ErrExecutableNotFound {
|
||||
t.Errorf("cmd.Run(): Expected error ErrExecutableNotFound but got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopBeforeStart(t *testing.T) {
|
||||
cmd := New().Command("echo", "hello")
|
||||
|
||||
// no panic calling Stop before calling Run
|
||||
cmd.Stop()
|
||||
|
||||
cmd.Run()
|
||||
|
||||
// no panic calling Stop after command is done
|
||||
cmd.Stop()
|
||||
}
|
||||
|
||||
func TestTimeout(t *testing.T) {
|
||||
exec := New()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
|
||||
defer cancel()
|
||||
|
||||
err := exec.CommandContext(ctx, "sleep", "2").Run()
|
||||
if err != context.DeadlineExceeded {
|
||||
t.Errorf("expected %v but got %v", context.DeadlineExceeded, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetEnv(t *testing.T) {
|
||||
ex := New()
|
||||
|
||||
out, err := ex.Command("/bin/sh", "-c", "echo $FOOBAR").CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %+v", err)
|
||||
}
|
||||
if string(out) != "\n" {
|
||||
t.Errorf("unexpected output: %q", string(out))
|
||||
}
|
||||
|
||||
cmd := ex.Command("/bin/sh", "-c", "echo $FOOBAR")
|
||||
cmd.SetEnv([]string{"FOOBAR=baz"})
|
||||
out, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %+v", err)
|
||||
}
|
||||
if string(out) != "baz\n" {
|
||||
t.Errorf("unexpected output: %q", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdIOPipes(t *testing.T) {
|
||||
cmd := New().Command("/bin/sh", "-c", "echo 'OUT'>&1; echo 'ERR'>&2")
|
||||
|
||||
stdoutPipe, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("expected StdoutPipe() not to error, got: %v", err)
|
||||
}
|
||||
stderrPipe, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("expected StderrPipe() not to error, got: %v", err)
|
||||
}
|
||||
|
||||
stdout := make(chan string)
|
||||
stderr := make(chan string)
|
||||
|
||||
go func() {
|
||||
stdout <- readAll(t, stdoutPipe, "StdOut")
|
||||
}()
|
||||
go func() {
|
||||
stderr <- readAll(t, stderrPipe, "StdErr")
|
||||
}()
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Errorf("expected Start() not to error, got: %v", err)
|
||||
}
|
||||
|
||||
if e, a := "OUT\n", <-stdout; e != a {
|
||||
t.Errorf("expected StdOut to be '%s', got: '%v'", e, a)
|
||||
}
|
||||
|
||||
if e, a := "ERR\n", <-stderr; e != a {
|
||||
t.Errorf("expected StdErr to be '%s', got: '%v'", e, a)
|
||||
}
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
t.Errorf("expected Wait() not to error, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func readAll(t *testing.T, r io.Reader, n string) string {
|
||||
t.Helper()
|
||||
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error when reading from %s: %v", n, err)
|
||||
}
|
||||
|
||||
return string(b)
|
||||
}
|
||||
37
vendor/k8s.io/utils/exec/new_test.go
generated
vendored
37
vendor/k8s.io/utils/exec/new_test.go
generated
vendored
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
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 exec_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
func ExampleNew() {
|
||||
exec := exec.New()
|
||||
|
||||
cmd := exec.Command("echo", "Bonjour!")
|
||||
buff := bytes.Buffer{}
|
||||
cmd.SetStdout(&buff)
|
||||
if err := cmd.Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(buff.String())
|
||||
// Output: Bonjour!
|
||||
}
|
||||
55
vendor/k8s.io/utils/exec/stdiopipe_test.go
generated
vendored
55
vendor/k8s.io/utils/exec/stdiopipe_test.go
generated
vendored
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
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 exec_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
func ExampleNew_stderrPipe() {
|
||||
cmd := exec.New().Command("/bin/sh", "-c", "echo 'We can read from stderr via pipe!' >&2")
|
||||
|
||||
stderrPipe, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stderr := make(chan []byte)
|
||||
go func() {
|
||||
b, err := ioutil.ReadAll(stderrPipe)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
stderr <- b
|
||||
}()
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
received := <-stderr
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(string(received))
|
||||
// Output: We can read from stderr via pipe!
|
||||
}
|
||||
252
vendor/k8s.io/utils/exec/testing/fake_exec.go
generated
vendored
252
vendor/k8s.io/utils/exec/testing/fake_exec.go
generated
vendored
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
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 testingexec
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
// FakeExec is a simple scripted Interface type.
|
||||
type FakeExec struct {
|
||||
CommandScript []FakeCommandAction
|
||||
CommandCalls int
|
||||
LookPathFunc func(string) (string, error)
|
||||
// ExactOrder enforces that commands are called in the order they are scripted,
|
||||
// and with the exact same arguments
|
||||
ExactOrder bool
|
||||
// DisableScripts removes the requirement that a slice of FakeCommandAction be
|
||||
// propulated before calling Command(). This makes the fakeexec (and subsequent
|
||||
// calls to Run() or CombinedOutput() always return success and there is no
|
||||
// ability to set their output.
|
||||
DisableScripts bool
|
||||
}
|
||||
|
||||
var _ exec.Interface = &FakeExec{}
|
||||
|
||||
// FakeCommandAction is the function to be executed
|
||||
type FakeCommandAction func(cmd string, args ...string) exec.Cmd
|
||||
|
||||
// Command is to track the commands that are executed
|
||||
func (fake *FakeExec) Command(cmd string, args ...string) exec.Cmd {
|
||||
if fake.DisableScripts {
|
||||
fakeCmd := &FakeCmd{DisableScripts: true}
|
||||
return InitFakeCmd(fakeCmd, cmd, args...)
|
||||
}
|
||||
if fake.CommandCalls > len(fake.CommandScript)-1 {
|
||||
panic(fmt.Sprintf("ran out of Command() actions. Could not handle command [%d]: %s args: %v", fake.CommandCalls, cmd, args))
|
||||
}
|
||||
i := fake.CommandCalls
|
||||
fake.CommandCalls++
|
||||
fakeCmd := fake.CommandScript[i](cmd, args...)
|
||||
if fake.ExactOrder {
|
||||
argv := append([]string{cmd}, args...)
|
||||
fc := fakeCmd.(*FakeCmd)
|
||||
if cmd != fc.Argv[0] {
|
||||
panic(fmt.Sprintf("received command: %s, expected: %s", cmd, fc.Argv[0]))
|
||||
}
|
||||
if len(argv) != len(fc.Argv) {
|
||||
panic(fmt.Sprintf("command (%s) received with extra/missing arguments. Expected %v, Received %v", cmd, fc.Argv, argv))
|
||||
}
|
||||
for i, a := range argv[1:] {
|
||||
if a != fc.Argv[i+1] {
|
||||
panic(fmt.Sprintf("command (%s) called with unexpected argument. Expected %s, Received %s", cmd, fc.Argv[i+1], a))
|
||||
}
|
||||
}
|
||||
}
|
||||
return fakeCmd
|
||||
}
|
||||
|
||||
// CommandContext wraps arguments into exec.Cmd
|
||||
func (fake *FakeExec) CommandContext(ctx context.Context, cmd string, args ...string) exec.Cmd {
|
||||
return fake.Command(cmd, args...)
|
||||
}
|
||||
|
||||
// LookPath is for finding the path of a file
|
||||
func (fake *FakeExec) LookPath(file string) (string, error) {
|
||||
return fake.LookPathFunc(file)
|
||||
}
|
||||
|
||||
// FakeCmd is a simple scripted Cmd type.
|
||||
type FakeCmd struct {
|
||||
Argv []string
|
||||
CombinedOutputScript []FakeCombinedOutputAction
|
||||
CombinedOutputCalls int
|
||||
CombinedOutputLog [][]string
|
||||
RunScript []FakeRunAction
|
||||
RunCalls int
|
||||
RunLog [][]string
|
||||
Dirs []string
|
||||
Stdin io.Reader
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
Env []string
|
||||
StdoutPipeResponse FakeStdIOPipeResponse
|
||||
StderrPipeResponse FakeStdIOPipeResponse
|
||||
WaitResponse error
|
||||
StartResponse error
|
||||
DisableScripts bool
|
||||
}
|
||||
|
||||
var _ exec.Cmd = &FakeCmd{}
|
||||
|
||||
// InitFakeCmd is for creating a fake exec.Cmd
|
||||
func InitFakeCmd(fake *FakeCmd, cmd string, args ...string) exec.Cmd {
|
||||
fake.Argv = append([]string{cmd}, args...)
|
||||
return fake
|
||||
}
|
||||
|
||||
// FakeStdIOPipeResponse holds responses to use as fakes for the StdoutPipe and
|
||||
// StderrPipe method calls
|
||||
type FakeStdIOPipeResponse struct {
|
||||
ReadCloser io.ReadCloser
|
||||
Error error
|
||||
}
|
||||
|
||||
// FakeCombinedOutputAction is a function type
|
||||
type FakeCombinedOutputAction func() ([]byte, error)
|
||||
|
||||
// FakeRunAction is a function type
|
||||
type FakeRunAction func() ([]byte, []byte, error)
|
||||
|
||||
// SetDir sets the directory
|
||||
func (fake *FakeCmd) SetDir(dir string) {
|
||||
fake.Dirs = append(fake.Dirs, dir)
|
||||
}
|
||||
|
||||
// SetStdin sets the stdin
|
||||
func (fake *FakeCmd) SetStdin(in io.Reader) {
|
||||
fake.Stdin = in
|
||||
}
|
||||
|
||||
// SetStdout sets the stdout
|
||||
func (fake *FakeCmd) SetStdout(out io.Writer) {
|
||||
fake.Stdout = out
|
||||
}
|
||||
|
||||
// SetStderr sets the stderr
|
||||
func (fake *FakeCmd) SetStderr(out io.Writer) {
|
||||
fake.Stderr = out
|
||||
}
|
||||
|
||||
// SetEnv sets the environment variables
|
||||
func (fake *FakeCmd) SetEnv(env []string) {
|
||||
fake.Env = env
|
||||
}
|
||||
|
||||
// StdoutPipe returns an injected ReadCloser & error (via StdoutPipeResponse)
|
||||
// to be able to inject an output stream on Stdout
|
||||
func (fake *FakeCmd) StdoutPipe() (io.ReadCloser, error) {
|
||||
return fake.StdoutPipeResponse.ReadCloser, fake.StdoutPipeResponse.Error
|
||||
}
|
||||
|
||||
// StderrPipe returns an injected ReadCloser & error (via StderrPipeResponse)
|
||||
// to be able to inject an output stream on Stderr
|
||||
func (fake *FakeCmd) StderrPipe() (io.ReadCloser, error) {
|
||||
return fake.StderrPipeResponse.ReadCloser, fake.StderrPipeResponse.Error
|
||||
}
|
||||
|
||||
// Start mimicks starting the process (in the background) and returns the
|
||||
// injected StartResponse
|
||||
func (fake *FakeCmd) Start() error {
|
||||
return fake.StartResponse
|
||||
}
|
||||
|
||||
// Wait mimicks waiting for the process to exit returns the
|
||||
// injected WaitResponse
|
||||
func (fake *FakeCmd) Wait() error {
|
||||
return fake.WaitResponse
|
||||
}
|
||||
|
||||
// Run runs the command
|
||||
func (fake *FakeCmd) Run() error {
|
||||
if fake.DisableScripts {
|
||||
return nil
|
||||
}
|
||||
if fake.RunCalls > len(fake.RunScript)-1 {
|
||||
panic("ran out of Run() actions")
|
||||
}
|
||||
if fake.RunLog == nil {
|
||||
fake.RunLog = [][]string{}
|
||||
}
|
||||
i := fake.RunCalls
|
||||
fake.RunLog = append(fake.RunLog, append([]string{}, fake.Argv...))
|
||||
fake.RunCalls++
|
||||
stdout, stderr, err := fake.RunScript[i]()
|
||||
if stdout != nil {
|
||||
fake.Stdout.Write(stdout)
|
||||
}
|
||||
if stderr != nil {
|
||||
fake.Stderr.Write(stderr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// CombinedOutput returns the output from the command
|
||||
func (fake *FakeCmd) CombinedOutput() ([]byte, error) {
|
||||
if fake.DisableScripts {
|
||||
return []byte{}, nil
|
||||
}
|
||||
if fake.CombinedOutputCalls > len(fake.CombinedOutputScript)-1 {
|
||||
panic("ran out of CombinedOutput() actions")
|
||||
}
|
||||
if fake.CombinedOutputLog == nil {
|
||||
fake.CombinedOutputLog = [][]string{}
|
||||
}
|
||||
i := fake.CombinedOutputCalls
|
||||
fake.CombinedOutputLog = append(fake.CombinedOutputLog, append([]string{}, fake.Argv...))
|
||||
fake.CombinedOutputCalls++
|
||||
return fake.CombinedOutputScript[i]()
|
||||
}
|
||||
|
||||
// Output is the response from the command
|
||||
func (fake *FakeCmd) Output() ([]byte, error) {
|
||||
return nil, fmt.Errorf("unimplemented")
|
||||
}
|
||||
|
||||
// Stop is to stop the process
|
||||
func (fake *FakeCmd) Stop() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
// FakeExitError is a simple fake ExitError type.
|
||||
type FakeExitError struct {
|
||||
Status int
|
||||
}
|
||||
|
||||
var _ exec.ExitError = FakeExitError{}
|
||||
|
||||
func (fake FakeExitError) String() string {
|
||||
return fmt.Sprintf("exit %d", fake.Status)
|
||||
}
|
||||
|
||||
func (fake FakeExitError) Error() string {
|
||||
return fake.String()
|
||||
}
|
||||
|
||||
// Exited always returns true
|
||||
func (fake FakeExitError) Exited() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// ExitStatus returns the fake status
|
||||
func (fake FakeExitError) ExitStatus() int {
|
||||
return fake.Status
|
||||
}
|
||||
137
vendor/k8s.io/utils/exec/testing/fake_exec_test.go
generated
vendored
137
vendor/k8s.io/utils/exec/testing/fake_exec_test.go
generated
vendored
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
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 testingexec
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
// Test that command order is enforced
|
||||
func TestCommandOrder(t *testing.T) {
|
||||
fe := getFakeExecWithScripts(true, false)
|
||||
|
||||
// If we call "cat" first, it should panic
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("The code did not panic")
|
||||
}
|
||||
}()
|
||||
fe.Command("cat")
|
||||
}
|
||||
|
||||
// Test that a command with different number of args panics
|
||||
func TestDiffNumArgs(t *testing.T) {
|
||||
fe := getFakeExecWithScripts(true, false)
|
||||
|
||||
// If we call "ps -e -f -A" instead of "ps -ef" it should panic
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("The code did not panic")
|
||||
}
|
||||
}()
|
||||
fe.Command("ps", "-e", "-f", "-A")
|
||||
}
|
||||
|
||||
// Test that a command with different args panics
|
||||
func TestDiffArgs(t *testing.T) {
|
||||
fe := getFakeExecWithScripts(true, false)
|
||||
|
||||
// If we call "ps -fe" instead of "ps -ef" it should panic
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("The code did not panic")
|
||||
}
|
||||
}()
|
||||
fe.Command("ps", "-fe")
|
||||
}
|
||||
|
||||
// Test that extra commands panics
|
||||
func TestExtraCommands(t *testing.T) {
|
||||
fe := getFakeExecWithScripts(false, false)
|
||||
|
||||
// If we call mnore calls than scripted, should panic
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("The code did not panic")
|
||||
}
|
||||
}()
|
||||
fe.Command("ps")
|
||||
fe.Command("cat")
|
||||
fe.Command("unscripted")
|
||||
}
|
||||
|
||||
// Test that calling a command without a script panics if not disabled
|
||||
func TestNoScriptPanic(t *testing.T) {
|
||||
fe := &FakeExec{}
|
||||
|
||||
// If we call mnore calls than scripted, should panic
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("The code did not panic")
|
||||
}
|
||||
}()
|
||||
fe.Command("ps")
|
||||
}
|
||||
|
||||
// Test that calling a command without a script does not panic if scripts
|
||||
// are disabled
|
||||
func TestNoScriptNoPanic(t *testing.T) {
|
||||
fe := &FakeExec{DisableScripts: true}
|
||||
|
||||
// If we call mnore calls than scripted, should panic
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("The code panic'd")
|
||||
}
|
||||
}()
|
||||
fe.Command("ps")
|
||||
}
|
||||
|
||||
func getFakeExecWithScripts(exactOrder bool, disableScripts bool) *FakeExec {
|
||||
scripts := []struct {
|
||||
cmd string
|
||||
args []string
|
||||
}{
|
||||
{
|
||||
cmd: "ps",
|
||||
args: []string{"-ef"},
|
||||
},
|
||||
{
|
||||
cmd: "cat",
|
||||
args: []string{"/var/log"},
|
||||
},
|
||||
}
|
||||
|
||||
fakeexec := &FakeExec{ExactOrder: exactOrder, DisableScripts: disableScripts}
|
||||
for _, s := range scripts {
|
||||
fakeCmd := &FakeCmd{}
|
||||
cmdAction := makeFakeCmd(fakeCmd, s.cmd, s.args...)
|
||||
fakeexec.CommandScript = append(fakeexec.CommandScript, cmdAction)
|
||||
}
|
||||
return fakeexec
|
||||
}
|
||||
|
||||
func makeFakeCmd(fakeCmd *FakeCmd, cmd string, args ...string) FakeCommandAction {
|
||||
c := cmd
|
||||
a := args
|
||||
return func(cmd string, args ...string) exec.Cmd {
|
||||
command := InitFakeCmd(fakeCmd, c, a...)
|
||||
return command
|
||||
}
|
||||
}
|
||||
91
vendor/k8s.io/utils/field/path.go
generated
vendored
91
vendor/k8s.io/utils/field/path.go
generated
vendored
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
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 field
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Path represents the path from some root to a particular field.
|
||||
type Path struct {
|
||||
name string // the name of this field or "" if this is an index
|
||||
index string // if name == "", this is a subscript (index or map key) of the previous element
|
||||
parent *Path // nil if this is the root element
|
||||
}
|
||||
|
||||
// NewPath creates a root Path object.
|
||||
func NewPath(name string, moreNames ...string) *Path {
|
||||
r := &Path{name: name, parent: nil}
|
||||
for _, anotherName := range moreNames {
|
||||
r = &Path{name: anotherName, parent: r}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Root returns the root element of this Path.
|
||||
func (p *Path) Root() *Path {
|
||||
for ; p.parent != nil; p = p.parent {
|
||||
// Do nothing.
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Child creates a new Path that is a child of the method receiver.
|
||||
func (p *Path) Child(name string, moreNames ...string) *Path {
|
||||
r := NewPath(name, moreNames...)
|
||||
r.Root().parent = p
|
||||
return r
|
||||
}
|
||||
|
||||
// Index indicates that the previous Path is to be subscripted by an int.
|
||||
// This sets the same underlying value as Key.
|
||||
func (p *Path) Index(index int) *Path {
|
||||
return &Path{index: strconv.Itoa(index), parent: p}
|
||||
}
|
||||
|
||||
// Key indicates that the previous Path is to be subscripted by a string.
|
||||
// This sets the same underlying value as Index.
|
||||
func (p *Path) Key(key string) *Path {
|
||||
return &Path{index: key, parent: p}
|
||||
}
|
||||
|
||||
// String produces a string representation of the Path.
|
||||
func (p *Path) String() string {
|
||||
// make a slice to iterate
|
||||
elems := []*Path{}
|
||||
for ; p != nil; p = p.parent {
|
||||
elems = append(elems, p)
|
||||
}
|
||||
|
||||
// iterate, but it has to be backwards
|
||||
buf := bytes.NewBuffer(nil)
|
||||
for i := range elems {
|
||||
p := elems[len(elems)-1-i]
|
||||
if p.parent != nil && len(p.name) > 0 {
|
||||
// This is either the root or it is a subscript.
|
||||
buf.WriteString(".")
|
||||
}
|
||||
if len(p.name) > 0 {
|
||||
buf.WriteString(p.name)
|
||||
} else {
|
||||
fmt.Fprintf(buf, "[%s]", p.index)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
123
vendor/k8s.io/utils/field/path_test.go
generated
vendored
123
vendor/k8s.io/utils/field/path_test.go
generated
vendored
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
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 field
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPath(t *testing.T) {
|
||||
testCases := []struct {
|
||||
op func(*Path) *Path
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
func(p *Path) *Path { return p },
|
||||
"root",
|
||||
},
|
||||
{
|
||||
func(p *Path) *Path { return p.Child("first") },
|
||||
"root.first",
|
||||
},
|
||||
{
|
||||
func(p *Path) *Path { return p.Child("second") },
|
||||
"root.first.second",
|
||||
},
|
||||
{
|
||||
func(p *Path) *Path { return p.Index(0) },
|
||||
"root.first.second[0]",
|
||||
},
|
||||
{
|
||||
func(p *Path) *Path { return p.Child("third") },
|
||||
"root.first.second[0].third",
|
||||
},
|
||||
{
|
||||
func(p *Path) *Path { return p.Index(93) },
|
||||
"root.first.second[0].third[93]",
|
||||
},
|
||||
{
|
||||
func(p *Path) *Path { return p.parent },
|
||||
"root.first.second[0].third",
|
||||
},
|
||||
{
|
||||
func(p *Path) *Path { return p.parent },
|
||||
"root.first.second[0]",
|
||||
},
|
||||
{
|
||||
func(p *Path) *Path { return p.Key("key") },
|
||||
"root.first.second[0][key]",
|
||||
},
|
||||
}
|
||||
|
||||
root := NewPath("root")
|
||||
p := root
|
||||
for i, tc := range testCases {
|
||||
p = tc.op(p)
|
||||
if p.String() != tc.expected {
|
||||
t.Errorf("[%d] Expected %q, got %q", i, tc.expected, p.String())
|
||||
}
|
||||
if p.Root() != root {
|
||||
t.Errorf("[%d] Wrong root: %#v", i, p.Root())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathMultiArg(t *testing.T) {
|
||||
testCases := []struct {
|
||||
op func(*Path) *Path
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
func(p *Path) *Path { return p },
|
||||
"root.first",
|
||||
},
|
||||
{
|
||||
func(p *Path) *Path { return p.Child("second", "third") },
|
||||
"root.first.second.third",
|
||||
},
|
||||
{
|
||||
func(p *Path) *Path { return p.Index(0) },
|
||||
"root.first.second.third[0]",
|
||||
},
|
||||
{
|
||||
func(p *Path) *Path { return p.parent },
|
||||
"root.first.second.third",
|
||||
},
|
||||
{
|
||||
func(p *Path) *Path { return p.parent },
|
||||
"root.first.second",
|
||||
},
|
||||
{
|
||||
func(p *Path) *Path { return p.parent },
|
||||
"root.first",
|
||||
},
|
||||
{
|
||||
func(p *Path) *Path { return p.parent },
|
||||
"root",
|
||||
},
|
||||
}
|
||||
|
||||
root := NewPath("root", "first")
|
||||
p := root
|
||||
for i, tc := range testCases {
|
||||
p = tc.op(p)
|
||||
if p.String() != tc.expected {
|
||||
t.Errorf("[%d] Expected %q, got %q", i, tc.expected, p.String())
|
||||
}
|
||||
if p.Root() != root.Root() {
|
||||
t.Errorf("[%d] Wrong root: %#v", i, p.Root())
|
||||
}
|
||||
}
|
||||
}
|
||||
10
vendor/k8s.io/utils/go.mod
generated
vendored
10
vendor/k8s.io/utils/go.mod
generated
vendored
@@ -1,10 +0,0 @@
|
||||
module k8s.io/utils
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/spf13/afero v1.2.2
|
||||
github.com/stretchr/testify v1.3.0
|
||||
k8s.io/klog v0.3.0
|
||||
)
|
||||
14
vendor/k8s.io/utils/go.sum
generated
vendored
14
vendor/k8s.io/utils/go.sum
generated
vendored
@@ -1,14 +0,0 @@
|
||||
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/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 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
k8s.io/klog v0.3.0 h1:0VPpR+sizsiivjIfIAQH/rl8tan6jvWkS7lU+0di3lE=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
2
vendor/k8s.io/utils/hack/.golint_failures
generated
vendored
2
vendor/k8s.io/utils/hack/.golint_failures
generated
vendored
@@ -1,2 +0,0 @@
|
||||
# Apart from this line, only trailing comments are supported
|
||||
mount
|
||||
46
vendor/k8s.io/utils/hack/lib/util.sh
generated
vendored
46
vendor/k8s.io/utils/hack/lib/util.sh
generated
vendored
@@ -1,46 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 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.
|
||||
|
||||
# arguments: target, item1, item2, item3, ...
|
||||
# returns 0 if target is in the given items, 1 otherwise.
|
||||
kube::util::array_contains() {
|
||||
local search="$1"
|
||||
local element
|
||||
shift
|
||||
for element; do
|
||||
if [[ "${element}" == "${search}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# kube::util::check-file-in-alphabetical-order <file>
|
||||
# Check that the file is in alphabetical order
|
||||
#
|
||||
function kube::util::check-file-in-alphabetical-order {
|
||||
local failure_file="$1"
|
||||
if ! diff -u "${failure_file}" <(LC_ALL=C sort "${failure_file}"); then
|
||||
{
|
||||
echo
|
||||
echo "${failure_file} is not in alphabetical order. Please sort it:"
|
||||
echo
|
||||
echo " LC_ALL=C sort -o ${failure_file} ${failure_file}"
|
||||
echo
|
||||
} >&2
|
||||
false
|
||||
fi
|
||||
}
|
||||
32
vendor/k8s.io/utils/hack/verify-gofmt.sh
generated
vendored
32
vendor/k8s.io/utils/hack/verify-gofmt.sh
generated
vendored
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 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.
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
if ! which gofmt > /dev/null; then
|
||||
echo "Can not find gofmt"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
diff=$(gofmt -s -d . 2>&1)
|
||||
if [[ -n "${diff}" ]]; then
|
||||
echo "${diff}"
|
||||
echo
|
||||
echo "Please run 'make update-fmt'"
|
||||
exit 1
|
||||
fi
|
||||
114
vendor/k8s.io/utils/hack/verify-golint.sh
generated
vendored
114
vendor/k8s.io/utils/hack/verify-golint.sh
generated
vendored
@@ -1,114 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 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.
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
|
||||
source "${KUBE_ROOT}/hack/lib/util.sh"
|
||||
|
||||
if ! which golint > /dev/null; then
|
||||
echo "installing golint"
|
||||
go get golang.org/x/lint/golint
|
||||
fi
|
||||
|
||||
cd "${KUBE_ROOT}"
|
||||
|
||||
# Check that the file is in alphabetical order
|
||||
failure_file="${KUBE_ROOT}/hack/.golint_failures"
|
||||
kube::util::check-file-in-alphabetical-order "${failure_file}"
|
||||
|
||||
export IFS=$'\n'
|
||||
# NOTE: when "go list -e ./..." is run within GOPATH, it turns the k8s.io/utils
|
||||
# as the prefix, however if we run it outside it returns the full path of the file
|
||||
# with a leading underscore. We'll need to support both scenarios for all_packages.
|
||||
all_packages=()
|
||||
while IFS='' read -r line; do all_packages+=("$line"); done < <(go list -e ./... | grep -vE "/(third_party)" | sed -e 's|^k8s.io/utils/||' -e "s|^_\(${KUBE_ROOT}/\)\{0,1\}||")
|
||||
# The regex below removes any "#" character and anything behind it and including any
|
||||
# whitespace before it. Then it removes empty lines.
|
||||
failing_packages=()
|
||||
while IFS='' read -r line; do failing_packages+=("$line"); done < <(sed -e 's/[[:blank:]]*#.*//' -e '/^$/d' "$failure_file")
|
||||
unset IFS
|
||||
errors=()
|
||||
not_failing=()
|
||||
for p in "${all_packages[@]}"; do
|
||||
# Run golint on package/*.go file explicitly to validate all go files
|
||||
# and not just the ones for the current platform. This also will ensure that
|
||||
# _test.go files are linted.
|
||||
# Generated files are ignored, and each file is passed through golint
|
||||
# individually, as if one file in the package contains a fatal error (such as
|
||||
# a foo package with a corresponding foo_test package), golint seems to choke
|
||||
# completely.
|
||||
# Ref: https://github.com/kubernetes/kubernetes/pull/67675
|
||||
# Ref: https://github.com/golang/lint/issues/68
|
||||
failedLint=$(find "$p"/*.go | xargs -L1 golint 2>/dev/null)
|
||||
kube::util::array_contains "$p" "${failing_packages[@]}" && in_failing=$? || in_failing=$?
|
||||
if [[ -n "${failedLint}" ]] && [[ "${in_failing}" -ne "0" ]]; then
|
||||
errors+=( "${failedLint}" )
|
||||
fi
|
||||
if [[ -z "${failedLint}" ]] && [[ "${in_failing}" -eq "0" ]]; then
|
||||
not_failing+=( "$p" )
|
||||
fi
|
||||
done
|
||||
|
||||
# Check that all failing_packages actually still exist
|
||||
gone=()
|
||||
for p in "${failing_packages[@]}"; do
|
||||
kube::util::array_contains "$p" "${all_packages[@]}" || gone+=( "$p" )
|
||||
done
|
||||
|
||||
# Check to be sure all the packages that should pass lint are.
|
||||
if [ ${#errors[@]} -eq 0 ]; then
|
||||
echo 'Congratulations! All Go source files have been linted.'
|
||||
else
|
||||
{
|
||||
echo "Errors from golint:"
|
||||
for err in "${errors[@]}"; do
|
||||
echo "$err"
|
||||
done
|
||||
echo
|
||||
echo 'Please review the above warnings. You can test via "golint" and commit the result.'
|
||||
echo 'If the above warnings do not make sense, you can exempt this package from golint'
|
||||
echo 'checking by adding it to hack/.golint_failures (if your reviewer is okay with it).'
|
||||
echo
|
||||
} >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ${#not_failing[@]} -gt 0 ]]; then
|
||||
{
|
||||
echo "Some packages in hack/.golint_failures are passing golint. Please remove them."
|
||||
echo
|
||||
for p in "${not_failing[@]}"; do
|
||||
echo " $p"
|
||||
done
|
||||
echo
|
||||
} >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ${#gone[@]} -gt 0 ]]; then
|
||||
{
|
||||
echo "Some packages in hack/.golint_failures do not exist anymore. Please remove them."
|
||||
echo
|
||||
for p in "${gone[@]}"; do
|
||||
echo " $p"
|
||||
done
|
||||
echo
|
||||
} >&2
|
||||
exit 1
|
||||
fi
|
||||
29
vendor/k8s.io/utils/hack/verify-govet.sh
generated
vendored
29
vendor/k8s.io/utils/hack/verify-govet.sh
generated
vendored
@@ -1,29 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 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.
|
||||
|
||||
# For use with Go 1.12+
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
GO_VERSION=${TRAVIS_GO_VERSION:-$(go version | cut -d ' ' -f 3)}
|
||||
|
||||
if [[ ${GO_VERSION} =~ ^(go)?1\.(9|10|11) ]]; then
|
||||
go tool vet .
|
||||
else
|
||||
go vet ./...
|
||||
fi
|
||||
27
vendor/k8s.io/utils/inotify/LICENSE
generated
vendored
27
vendor/k8s.io/utils/inotify/LICENSE
generated
vendored
@@ -1,27 +0,0 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
22
vendor/k8s.io/utils/inotify/PATENTS
generated
vendored
22
vendor/k8s.io/utils/inotify/PATENTS
generated
vendored
@@ -1,22 +0,0 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google 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,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
5
vendor/k8s.io/utils/inotify/README.md
generated
vendored
5
vendor/k8s.io/utils/inotify/README.md
generated
vendored
@@ -1,5 +0,0 @@
|
||||
This is a fork of golang.org/x/exp/inotify before it was deleted.
|
||||
|
||||
Please use gopkg.in/fsnotify.v0 instead.
|
||||
|
||||
For updates, see: https://fsnotify.org/
|
||||
333
vendor/k8s.io/utils/inotify/inotify_linux.go
generated
vendored
333
vendor/k8s.io/utils/inotify/inotify_linux.go
generated
vendored
@@ -1,333 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package inotify implements a wrapper for the Linux inotify system.
|
||||
|
||||
Example:
|
||||
watcher, err := inotify.NewWatcher()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = watcher.Watch("/tmp")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case ev := <-watcher.Event:
|
||||
log.Println("event:", ev)
|
||||
case err := <-watcher.Error:
|
||||
log.Println("error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
package inotify // import "k8s.io/utils/inotify"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Event represents a notification
|
||||
type Event struct {
|
||||
Mask uint32 // Mask of events
|
||||
Cookie uint32 // Unique cookie associating related events (for rename(2))
|
||||
Name string // File name (optional)
|
||||
}
|
||||
|
||||
type watch struct {
|
||||
wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
|
||||
flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
|
||||
}
|
||||
|
||||
// Watcher represents an inotify instance
|
||||
type Watcher struct {
|
||||
mu sync.Mutex
|
||||
fd int // File descriptor (as returned by the inotify_init() syscall)
|
||||
watches map[string]*watch // Map of inotify watches (key: path)
|
||||
paths map[int]string // Map of watched paths (key: watch descriptor)
|
||||
Error chan error // Errors are sent on this channel
|
||||
Event chan *Event // Events are returned on this channel
|
||||
done chan bool // Channel for sending a "quit message" to the reader goroutine
|
||||
isClosed bool // Set to true when Close() is first called
|
||||
}
|
||||
|
||||
// NewWatcher creates and returns a new inotify instance using inotify_init(2)
|
||||
func NewWatcher() (*Watcher, error) {
|
||||
fd, errno := syscall.InotifyInit1(syscall.IN_CLOEXEC)
|
||||
if fd == -1 {
|
||||
return nil, os.NewSyscallError("inotify_init", errno)
|
||||
}
|
||||
w := &Watcher{
|
||||
fd: fd,
|
||||
watches: make(map[string]*watch),
|
||||
paths: make(map[int]string),
|
||||
Event: make(chan *Event),
|
||||
Error: make(chan error),
|
||||
done: make(chan bool, 1),
|
||||
}
|
||||
|
||||
go w.readEvents()
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// Close closes an inotify watcher instance
|
||||
// It sends a message to the reader goroutine to quit and removes all watches
|
||||
// associated with the inotify instance
|
||||
func (w *Watcher) Close() error {
|
||||
if w.isClosed {
|
||||
return nil
|
||||
}
|
||||
w.isClosed = true
|
||||
|
||||
// Send "quit" message to the reader goroutine
|
||||
w.done <- true
|
||||
for path := range w.watches {
|
||||
w.RemoveWatch(path)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddWatch adds path to the watched file set.
|
||||
// The flags are interpreted as described in inotify_add_watch(2).
|
||||
func (w *Watcher) AddWatch(path string, flags uint32) error {
|
||||
if w.isClosed {
|
||||
return errors.New("inotify instance already closed")
|
||||
}
|
||||
|
||||
watchEntry, found := w.watches[path]
|
||||
if found {
|
||||
watchEntry.flags |= flags
|
||||
flags |= syscall.IN_MASK_ADD
|
||||
}
|
||||
|
||||
w.mu.Lock() // synchronize with readEvents goroutine
|
||||
|
||||
wd, err := syscall.InotifyAddWatch(w.fd, path, flags)
|
||||
if err != nil {
|
||||
w.mu.Unlock()
|
||||
return &os.PathError{
|
||||
Op: "inotify_add_watch",
|
||||
Path: path,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
w.watches[path] = &watch{wd: uint32(wd), flags: flags}
|
||||
w.paths[wd] = path
|
||||
}
|
||||
w.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Watch adds path to the watched file set, watching all events.
|
||||
func (w *Watcher) Watch(path string) error {
|
||||
return w.AddWatch(path, InAllEvents)
|
||||
}
|
||||
|
||||
// RemoveWatch removes path from the watched file set.
|
||||
func (w *Watcher) RemoveWatch(path string) error {
|
||||
watch, ok := w.watches[path]
|
||||
if !ok {
|
||||
return fmt.Errorf("can't remove non-existent inotify watch for: %s", path)
|
||||
}
|
||||
success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
|
||||
if success == -1 {
|
||||
return os.NewSyscallError("inotify_rm_watch", errno)
|
||||
}
|
||||
delete(w.watches, path)
|
||||
// Locking here to protect the read from paths in readEvents.
|
||||
w.mu.Lock()
|
||||
delete(w.paths, int(watch.wd))
|
||||
w.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// readEvents reads from the inotify file descriptor, converts the
|
||||
// received events into Event objects and sends them via the Event channel
|
||||
func (w *Watcher) readEvents() {
|
||||
var buf [syscall.SizeofInotifyEvent * 4096]byte
|
||||
|
||||
for {
|
||||
n, err := syscall.Read(w.fd, buf[:])
|
||||
// See if there is a message on the "done" channel
|
||||
var done bool
|
||||
select {
|
||||
case done = <-w.done:
|
||||
default:
|
||||
}
|
||||
|
||||
// If EOF or a "done" message is received
|
||||
if n == 0 || done {
|
||||
// The syscall.Close can be slow. Close
|
||||
// w.Event first.
|
||||
close(w.Event)
|
||||
err := syscall.Close(w.fd)
|
||||
if err != nil {
|
||||
w.Error <- os.NewSyscallError("close", err)
|
||||
}
|
||||
close(w.Error)
|
||||
return
|
||||
}
|
||||
if n < 0 {
|
||||
w.Error <- os.NewSyscallError("read", err)
|
||||
continue
|
||||
}
|
||||
if n < syscall.SizeofInotifyEvent {
|
||||
w.Error <- errors.New("inotify: short read in readEvents()")
|
||||
continue
|
||||
}
|
||||
|
||||
var offset uint32
|
||||
// We don't know how many events we just read into the buffer
|
||||
// While the offset points to at least one whole event...
|
||||
for offset <= uint32(n-syscall.SizeofInotifyEvent) {
|
||||
// Point "raw" to the event in the buffer
|
||||
raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
|
||||
event := new(Event)
|
||||
event.Mask = uint32(raw.Mask)
|
||||
event.Cookie = uint32(raw.Cookie)
|
||||
nameLen := uint32(raw.Len)
|
||||
// If the event happened to the watched directory or the watched file, the kernel
|
||||
// doesn't append the filename to the event, but we would like to always fill the
|
||||
// the "Name" field with a valid filename. We retrieve the path of the watch from
|
||||
// the "paths" map.
|
||||
w.mu.Lock()
|
||||
name, ok := w.paths[int(raw.Wd)]
|
||||
w.mu.Unlock()
|
||||
if ok {
|
||||
event.Name = name
|
||||
if nameLen > 0 {
|
||||
// Point "bytes" at the first byte of the filename
|
||||
bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
|
||||
// The filename is padded with NUL bytes. TrimRight() gets rid of those.
|
||||
event.Name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
|
||||
}
|
||||
// Send the event on the events channel
|
||||
w.Event <- event
|
||||
}
|
||||
// Move to the next event in the buffer
|
||||
offset += syscall.SizeofInotifyEvent + nameLen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String formats the event e in the form
|
||||
// "filename: 0xEventMask = IN_ACCESS|IN_ATTRIB_|..."
|
||||
func (e *Event) String() string {
|
||||
var events string
|
||||
|
||||
m := e.Mask
|
||||
for _, b := range eventBits {
|
||||
if m&b.Value == b.Value {
|
||||
m &^= b.Value
|
||||
events += "|" + b.Name
|
||||
}
|
||||
}
|
||||
|
||||
if m != 0 {
|
||||
events += fmt.Sprintf("|%#x", m)
|
||||
}
|
||||
if len(events) > 0 {
|
||||
events = " == " + events[1:]
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%q: %#x%s", e.Name, e.Mask, events)
|
||||
}
|
||||
|
||||
const (
|
||||
// Options for inotify_init() are not exported
|
||||
// IN_CLOEXEC uint32 = syscall.IN_CLOEXEC
|
||||
// IN_NONBLOCK uint32 = syscall.IN_NONBLOCK
|
||||
|
||||
// Options for AddWatch
|
||||
|
||||
// InDontFollow : Don't dereference pathname if it is a symbolic link
|
||||
InDontFollow uint32 = syscall.IN_DONT_FOLLOW
|
||||
// InOneshot : Monitor the filesystem object corresponding to pathname for one event, then remove from watch list
|
||||
InOneshot uint32 = syscall.IN_ONESHOT
|
||||
// InOnlydir : Watch pathname only if it is a directory
|
||||
InOnlydir uint32 = syscall.IN_ONLYDIR
|
||||
|
||||
// The "IN_MASK_ADD" option is not exported, as AddWatch
|
||||
// adds it automatically, if there is already a watch for the given path
|
||||
// IN_MASK_ADD uint32 = syscall.IN_MASK_ADD
|
||||
|
||||
// Events
|
||||
|
||||
// InAccess : File was accessed
|
||||
InAccess uint32 = syscall.IN_ACCESS
|
||||
// InAllEvents : Bit mask for all notify events
|
||||
InAllEvents uint32 = syscall.IN_ALL_EVENTS
|
||||
// InAttrib : Metadata changed
|
||||
InAttrib uint32 = syscall.IN_ATTRIB
|
||||
// InClose : Equates to IN_CLOSE_WRITE | IN_CLOSE_NOWRITE
|
||||
InClose uint32 = syscall.IN_CLOSE
|
||||
// InCloseNowrite : File or directory not opened for writing was closed
|
||||
InCloseNowrite uint32 = syscall.IN_CLOSE_NOWRITE
|
||||
// InCloseWrite : File opened for writing was closed
|
||||
InCloseWrite uint32 = syscall.IN_CLOSE_WRITE
|
||||
// InCreate : File/directory created in watched directory
|
||||
InCreate uint32 = syscall.IN_CREATE
|
||||
// InDelete : File/directory deleted from watched directory
|
||||
InDelete uint32 = syscall.IN_DELETE
|
||||
// InDeleteSelf : Watched file/directory was itself deleted
|
||||
InDeleteSelf uint32 = syscall.IN_DELETE_SELF
|
||||
// InModify : File was modified
|
||||
InModify uint32 = syscall.IN_MODIFY
|
||||
// InMove : Equates to IN_MOVED_FROM | IN_MOVED_TO
|
||||
InMove uint32 = syscall.IN_MOVE
|
||||
// InMovedFrom : Generated for the directory containing the old filename when a file is renamed
|
||||
InMovedFrom uint32 = syscall.IN_MOVED_FROM
|
||||
// InMovedTo : Generated for the directory containing the new filename when a file is renamed
|
||||
InMovedTo uint32 = syscall.IN_MOVED_TO
|
||||
// InMoveSelf : Watched file/directory was itself moved
|
||||
InMoveSelf uint32 = syscall.IN_MOVE_SELF
|
||||
// InOpen : File or directory was opened
|
||||
InOpen uint32 = syscall.IN_OPEN
|
||||
|
||||
// Special events
|
||||
|
||||
// InIsdir : Subject of this event is a directory
|
||||
InIsdir uint32 = syscall.IN_ISDIR
|
||||
// InIgnored : Watch was removed explicitly or automatically
|
||||
InIgnored uint32 = syscall.IN_IGNORED
|
||||
// InQOverflow : Event queue overflowed
|
||||
InQOverflow uint32 = syscall.IN_Q_OVERFLOW
|
||||
// InUnmount : Filesystem containing watched object was unmounted
|
||||
InUnmount uint32 = syscall.IN_UNMOUNT
|
||||
)
|
||||
|
||||
var eventBits = []struct {
|
||||
Value uint32
|
||||
Name string
|
||||
}{
|
||||
{InAccess, "IN_ACCESS"},
|
||||
{InAttrib, "IN_ATTRIB"},
|
||||
{InClose, "IN_CLOSE"},
|
||||
{InCloseNowrite, "IN_CLOSE_NOWRITE"},
|
||||
{InCloseWrite, "IN_CLOSE_WRITE"},
|
||||
{InCreate, "IN_CREATE"},
|
||||
{InDelete, "IN_DELETE"},
|
||||
{InDeleteSelf, "IN_DELETE_SELF"},
|
||||
{InModify, "IN_MODIFY"},
|
||||
{InMove, "IN_MOVE"},
|
||||
{InMovedFrom, "IN_MOVED_FROM"},
|
||||
{InMovedTo, "IN_MOVED_TO"},
|
||||
{InMoveSelf, "IN_MOVE_SELF"},
|
||||
{InOpen, "IN_OPEN"},
|
||||
{InIsdir, "IN_ISDIR"},
|
||||
{InIgnored, "IN_IGNORED"},
|
||||
{InQOverflow, "IN_Q_OVERFLOW"},
|
||||
{InUnmount, "IN_UNMOUNT"},
|
||||
}
|
||||
107
vendor/k8s.io/utils/inotify/inotify_linux_test.go
generated
vendored
107
vendor/k8s.io/utils/inotify/inotify_linux_test.go
generated
vendored
@@ -1,107 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux
|
||||
|
||||
package inotify
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestInotifyEvents(t *testing.T) {
|
||||
// Create an inotify watcher instance and initialize it
|
||||
watcher, err := NewWatcher()
|
||||
if err != nil {
|
||||
t.Fatalf("NewWatcher failed: %s", err)
|
||||
}
|
||||
|
||||
dir, err := ioutil.TempDir("", "inotify")
|
||||
if err != nil {
|
||||
t.Fatalf("TempDir failed: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// Add a watch for "_test"
|
||||
err = watcher.Watch(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("Watch failed: %s", err)
|
||||
}
|
||||
|
||||
// Receive errors on the error channel on a separate goroutine
|
||||
go func() {
|
||||
for err := range watcher.Error {
|
||||
t.Fatalf("error received: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
testFile := dir + "/TestInotifyEvents.testfile"
|
||||
|
||||
// Receive events on the event channel on a separate goroutine
|
||||
eventstream := watcher.Event
|
||||
var eventsReceived int32
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
for event := range eventstream {
|
||||
// Only count relevant events
|
||||
if event.Name == testFile {
|
||||
atomic.AddInt32(&eventsReceived, 1)
|
||||
t.Logf("event received: %s", event)
|
||||
} else {
|
||||
t.Logf("unexpected event received: %s", event)
|
||||
}
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
|
||||
// Create a file
|
||||
// This should add at least one event to the inotify event queue
|
||||
_, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
t.Fatalf("creating test file: %s", err)
|
||||
}
|
||||
|
||||
// We expect this event to be received almost immediately, but let's wait 1 s to be sure
|
||||
time.Sleep(1 * time.Second)
|
||||
if atomic.AddInt32(&eventsReceived, 0) == 0 {
|
||||
t.Fatal("inotify event hasn't been received after 1 second")
|
||||
}
|
||||
|
||||
// Try closing the inotify instance
|
||||
t.Log("calling Close()")
|
||||
watcher.Close()
|
||||
t.Log("waiting for the event channel to become closed...")
|
||||
select {
|
||||
case <-done:
|
||||
t.Log("event channel closed")
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("event stream was not closed after 1 second")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInotifyClose(t *testing.T) {
|
||||
watcher, _ := NewWatcher()
|
||||
watcher.Close()
|
||||
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
watcher.Close()
|
||||
done <- true
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
t.Fatal("double Close() test failed: second Close() call didn't return")
|
||||
}
|
||||
|
||||
err := watcher.Watch(os.TempDir())
|
||||
if err == nil {
|
||||
t.Fatal("expected error on Watch() after Close(), got nil")
|
||||
}
|
||||
}
|
||||
73
vendor/k8s.io/utils/integer/integer.go
generated
vendored
73
vendor/k8s.io/utils/integer/integer.go
generated
vendored
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
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 integer
|
||||
|
||||
// IntMax returns the maximum of the params
|
||||
func IntMax(a, b int) int {
|
||||
if b > a {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IntMin returns the minimum of the params
|
||||
func IntMin(a, b int) int {
|
||||
if b < a {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Int32Max returns the maximum of the params
|
||||
func Int32Max(a, b int32) int32 {
|
||||
if b > a {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Int32Min returns the minimum of the params
|
||||
func Int32Min(a, b int32) int32 {
|
||||
if b < a {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Int64Max returns the maximum of the params
|
||||
func Int64Max(a, b int64) int64 {
|
||||
if b > a {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Int64Min returns the minimum of the params
|
||||
func Int64Min(a, b int64) int64 {
|
||||
if b < a {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// RoundToInt32 rounds floats into integer numbers.
|
||||
func RoundToInt32(a float64) int32 {
|
||||
if a < 0 {
|
||||
return int32(a - 0.5)
|
||||
}
|
||||
return int32(a + 0.5)
|
||||
}
|
||||
244
vendor/k8s.io/utils/integer/integer_test.go
generated
vendored
244
vendor/k8s.io/utils/integer/integer_test.go
generated
vendored
@@ -1,244 +0,0 @@
|
||||
/*
|
||||
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 integer
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestIntMax(t *testing.T) {
|
||||
tests := []struct {
|
||||
nums []int
|
||||
expectedMax int
|
||||
}{
|
||||
{
|
||||
nums: []int{-1, 0},
|
||||
expectedMax: 0,
|
||||
},
|
||||
{
|
||||
nums: []int{-1, -2},
|
||||
expectedMax: -1,
|
||||
},
|
||||
{
|
||||
nums: []int{0, 1},
|
||||
expectedMax: 1,
|
||||
},
|
||||
{
|
||||
nums: []int{1, 2},
|
||||
expectedMax: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("executing scenario %d", i)
|
||||
if max := IntMax(test.nums[0], test.nums[1]); max != test.expectedMax {
|
||||
t.Errorf("expected %v, got %v", test.expectedMax, max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntMin(t *testing.T) {
|
||||
tests := []struct {
|
||||
nums []int
|
||||
expectedMin int
|
||||
}{
|
||||
{
|
||||
nums: []int{-1, 0},
|
||||
expectedMin: -1,
|
||||
},
|
||||
{
|
||||
nums: []int{-1, -2},
|
||||
expectedMin: -2,
|
||||
},
|
||||
{
|
||||
nums: []int{0, 1},
|
||||
expectedMin: 0,
|
||||
},
|
||||
{
|
||||
nums: []int{1, 2},
|
||||
expectedMin: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("executing scenario %d", i)
|
||||
if min := IntMin(test.nums[0], test.nums[1]); min != test.expectedMin {
|
||||
t.Errorf("expected %v, got %v", test.expectedMin, min)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt32Max(t *testing.T) {
|
||||
tests := []struct {
|
||||
nums []int32
|
||||
expectedMax int32
|
||||
}{
|
||||
{
|
||||
nums: []int32{-1, 0},
|
||||
expectedMax: 0,
|
||||
},
|
||||
{
|
||||
nums: []int32{-1, -2},
|
||||
expectedMax: -1,
|
||||
},
|
||||
{
|
||||
nums: []int32{0, 1},
|
||||
expectedMax: 1,
|
||||
},
|
||||
{
|
||||
nums: []int32{1, 2},
|
||||
expectedMax: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("executing scenario %d", i)
|
||||
if max := Int32Max(test.nums[0], test.nums[1]); max != test.expectedMax {
|
||||
t.Errorf("expected %v, got %v", test.expectedMax, max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt32Min(t *testing.T) {
|
||||
tests := []struct {
|
||||
nums []int32
|
||||
expectedMin int32
|
||||
}{
|
||||
{
|
||||
nums: []int32{-1, 0},
|
||||
expectedMin: -1,
|
||||
},
|
||||
{
|
||||
nums: []int32{-1, -2},
|
||||
expectedMin: -2,
|
||||
},
|
||||
{
|
||||
nums: []int32{0, 1},
|
||||
expectedMin: 0,
|
||||
},
|
||||
{
|
||||
nums: []int32{1, 2},
|
||||
expectedMin: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("executing scenario %d", i)
|
||||
if min := Int32Min(test.nums[0], test.nums[1]); min != test.expectedMin {
|
||||
t.Errorf("expected %v, got %v", test.expectedMin, min)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt64Max(t *testing.T) {
|
||||
tests := []struct {
|
||||
nums []int64
|
||||
expectedMax int64
|
||||
}{
|
||||
{
|
||||
nums: []int64{-1, 0},
|
||||
expectedMax: 0,
|
||||
},
|
||||
{
|
||||
nums: []int64{-1, -2},
|
||||
expectedMax: -1,
|
||||
},
|
||||
{
|
||||
nums: []int64{0, 1},
|
||||
expectedMax: 1,
|
||||
},
|
||||
{
|
||||
nums: []int64{1, 2},
|
||||
expectedMax: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("executing scenario %d", i)
|
||||
if max := Int64Max(test.nums[0], test.nums[1]); max != test.expectedMax {
|
||||
t.Errorf("expected %v, got %v", test.expectedMax, max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt64Min(t *testing.T) {
|
||||
tests := []struct {
|
||||
nums []int64
|
||||
expectedMin int64
|
||||
}{
|
||||
{
|
||||
nums: []int64{-1, 0},
|
||||
expectedMin: -1,
|
||||
},
|
||||
{
|
||||
nums: []int64{-1, -2},
|
||||
expectedMin: -2,
|
||||
},
|
||||
{
|
||||
nums: []int64{0, 1},
|
||||
expectedMin: 0,
|
||||
},
|
||||
{
|
||||
nums: []int64{1, 2},
|
||||
expectedMin: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("executing scenario %d", i)
|
||||
if min := Int64Min(test.nums[0], test.nums[1]); min != test.expectedMin {
|
||||
t.Errorf("expected %v, got %v", test.expectedMin, min)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundToInt32(t *testing.T) {
|
||||
tests := []struct {
|
||||
num float64
|
||||
exp int32
|
||||
}{
|
||||
{
|
||||
num: 5.5,
|
||||
exp: 6,
|
||||
},
|
||||
{
|
||||
num: -3.7,
|
||||
exp: -4,
|
||||
},
|
||||
{
|
||||
num: 3.49,
|
||||
exp: 3,
|
||||
},
|
||||
{
|
||||
num: -7.9,
|
||||
exp: -8,
|
||||
},
|
||||
{
|
||||
num: -4.499999,
|
||||
exp: -4,
|
||||
},
|
||||
{
|
||||
num: 0,
|
||||
exp: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("executing scenario %d", i)
|
||||
if got := RoundToInt32(test.num); got != test.exp {
|
||||
t.Errorf("expected %d, got %d", test.exp, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
74
vendor/k8s.io/utils/io/read.go
generated
vendored
74
vendor/k8s.io/utils/io/read.go
generated
vendored
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
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 io
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// ErrLimitReached means that the read limit is reached.
|
||||
var ErrLimitReached = errors.New("the read limit is reached")
|
||||
|
||||
// ConsistentRead repeatedly reads a file until it gets the same content twice.
|
||||
// This is useful when reading files in /proc that are larger than page size
|
||||
// and kernel may modify them between individual read() syscalls.
|
||||
func ConsistentRead(filename string, attempts int) ([]byte, error) {
|
||||
return consistentReadSync(filename, attempts, nil)
|
||||
}
|
||||
|
||||
// consistentReadSync is the main functionality of ConsistentRead but
|
||||
// introduces a sync callback that can be used by the tests to mutate the file
|
||||
// from which the test data is being read
|
||||
func consistentReadSync(filename string, attempts int, sync func(int)) ([]byte, error) {
|
||||
oldContent, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < attempts; i++ {
|
||||
if sync != nil {
|
||||
sync(i)
|
||||
}
|
||||
newContent, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bytes.Compare(oldContent, newContent) == 0 {
|
||||
return newContent, nil
|
||||
}
|
||||
// Files are different, continue reading
|
||||
oldContent = newContent
|
||||
}
|
||||
return nil, fmt.Errorf("could not get consistent content of %s after %d attempts", filename, attempts)
|
||||
}
|
||||
|
||||
// ReadAtMost reads up to `limit` bytes from `r`, and reports an error
|
||||
// when `limit` bytes are read.
|
||||
func ReadAtMost(r io.Reader, limit int64) ([]byte, error) {
|
||||
limitedReader := &io.LimitedReader{R: r, N: limit}
|
||||
data, err := ioutil.ReadAll(limitedReader)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
if limitedReader.N <= 0 {
|
||||
return data, ErrLimitReached
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
123
vendor/k8s.io/utils/io/read_test.go
generated
vendored
123
vendor/k8s.io/utils/io/read_test.go
generated
vendored
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
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 io
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func writeToPipe(namedPipe string, flaky bool, i int) {
|
||||
pipe, err := os.OpenFile(namedPipe, os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// The first two reads should never be consistent but all
|
||||
// subsequent reads should be
|
||||
outstr := fmt.Sprintf("Foobar %t", (i <= 0))
|
||||
|
||||
if flaky {
|
||||
outstr = fmt.Sprintf("Foobar %d", i)
|
||||
}
|
||||
|
||||
pipe.Write([]byte(outstr))
|
||||
pipe.Close()
|
||||
}
|
||||
|
||||
func makePipe(t *testing.T) string {
|
||||
tmp, err := ioutil.TempDir("", "pipe-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pipe := filepath.Join(tmp, "pipe")
|
||||
syscall.Mkfifo(pipe, 0600)
|
||||
|
||||
return pipe
|
||||
}
|
||||
|
||||
func writer(namedPipe string, flaky bool, c <-chan int, d <-chan bool) {
|
||||
// Make sure something is in the fifo otherwise the first iteration of
|
||||
// ConsistentRead will block forever
|
||||
writeToPipe(namedPipe, flaky, -1)
|
||||
|
||||
for {
|
||||
select {
|
||||
case i := <-c:
|
||||
writeToPipe(namedPipe, flaky, i)
|
||||
case <-d:
|
||||
os.RemoveAll(namedPipe)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConsistentRead(t *testing.T) {
|
||||
pipe := makePipe(t)
|
||||
prog, done := make(chan int), make(chan bool)
|
||||
go writer(pipe, false, prog, done)
|
||||
|
||||
if _, err := consistentReadSync(pipe, 3, func(i int) { prog <- i }); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
done <- true
|
||||
}
|
||||
|
||||
func TestConsistentReadFlakyReader(t *testing.T) {
|
||||
pipe := makePipe(t)
|
||||
prog, done := make(chan int), make(chan bool)
|
||||
go writer(pipe, true, prog, done)
|
||||
|
||||
if _, err := consistentReadSync(pipe, 3, func(i int) { prog <- i }); err == nil {
|
||||
t.Fatal("flaky reader returned consistent results")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadAtMost(t *testing.T) {
|
||||
testCases := []struct {
|
||||
limit int64
|
||||
data string
|
||||
errMsg string
|
||||
}{
|
||||
{4, "hell", "the read limit is reached"},
|
||||
{5, "hello", "the read limit is reached"},
|
||||
{6, "hello", ""},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
r := strings.NewReader("hello")
|
||||
data, err := ReadAtMost(r, tc.limit)
|
||||
if string(data) != tc.data {
|
||||
t.Errorf("Read limit %d: expected \"%s\", got \"%s\"", tc.limit, tc.data, string(data))
|
||||
}
|
||||
|
||||
if err == nil && tc.errMsg != "" {
|
||||
t.Errorf("Read limit %d: expected error with message \"%s\", got no error", tc.limit, tc.errMsg)
|
||||
}
|
||||
|
||||
if err != nil && err.Error() != tc.errMsg {
|
||||
t.Errorf("Read limit %d: expected error with message \"%s\", got error with message \"%s\"", tc.limit, tc.errMsg, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
58
vendor/k8s.io/utils/keymutex/hashed.go
generated
vendored
58
vendor/k8s.io/utils/keymutex/hashed.go
generated
vendored
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
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 keymutex
|
||||
|
||||
import (
|
||||
"hash/fnv"
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// NewHashed returns a new instance of KeyMutex which hashes arbitrary keys to
|
||||
// a fixed set of locks. `n` specifies number of locks, if n <= 0, we use
|
||||
// number of cpus.
|
||||
// Note that because it uses fixed set of locks, different keys may share same
|
||||
// lock, so it's possible to wait on same lock.
|
||||
func NewHashed(n int) KeyMutex {
|
||||
if n <= 0 {
|
||||
n = runtime.NumCPU()
|
||||
}
|
||||
return &hashedKeyMutex{
|
||||
mutexes: make([]sync.Mutex, n),
|
||||
}
|
||||
}
|
||||
|
||||
type hashedKeyMutex struct {
|
||||
mutexes []sync.Mutex
|
||||
}
|
||||
|
||||
// Acquires a lock associated with the specified ID.
|
||||
func (km *hashedKeyMutex) LockKey(id string) {
|
||||
km.mutexes[km.hash(id)%uint32(len(km.mutexes))].Lock()
|
||||
}
|
||||
|
||||
// Releases the lock associated with the specified ID.
|
||||
func (km *hashedKeyMutex) UnlockKey(id string) error {
|
||||
km.mutexes[km.hash(id)%uint32(len(km.mutexes))].Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (km *hashedKeyMutex) hash(id string) uint32 {
|
||||
h := fnv.New32a()
|
||||
h.Write([]byte(id))
|
||||
return h.Sum32()
|
||||
}
|
||||
27
vendor/k8s.io/utils/keymutex/keymutex.go
generated
vendored
27
vendor/k8s.io/utils/keymutex/keymutex.go
generated
vendored
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
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 keymutex
|
||||
|
||||
// KeyMutex is a thread-safe interface for acquiring locks on arbitrary strings.
|
||||
type KeyMutex interface {
|
||||
// Acquires a lock associated with the specified ID, creates the lock if one doesn't already exist.
|
||||
LockKey(id string)
|
||||
|
||||
// Releases the lock associated with the specified ID.
|
||||
// Returns an error if the specified ID doesn't exist.
|
||||
UnlockKey(id string) error
|
||||
}
|
||||
105
vendor/k8s.io/utils/keymutex/keymutex_test.go
generated
vendored
105
vendor/k8s.io/utils/keymutex/keymutex_test.go
generated
vendored
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
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 keymutex
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
callbackTimeout = 1 * time.Second
|
||||
)
|
||||
|
||||
func newKeyMutexes() []KeyMutex {
|
||||
return []KeyMutex{
|
||||
NewHashed(0),
|
||||
NewHashed(1),
|
||||
NewHashed(2),
|
||||
NewHashed(4),
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SingleLock_NoUnlock(t *testing.T) {
|
||||
for _, km := range newKeyMutexes() {
|
||||
// Arrange
|
||||
key := "fakeid"
|
||||
callbackCh := make(chan interface{})
|
||||
|
||||
// Act
|
||||
go lockAndCallback(km, key, callbackCh)
|
||||
|
||||
// Assert
|
||||
verifyCallbackHappens(t, callbackCh)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SingleLock_SingleUnlock(t *testing.T) {
|
||||
for _, km := range newKeyMutexes() {
|
||||
// Arrange
|
||||
key := "fakeid"
|
||||
callbackCh := make(chan interface{})
|
||||
|
||||
// Act & Assert
|
||||
go lockAndCallback(km, key, callbackCh)
|
||||
verifyCallbackHappens(t, callbackCh)
|
||||
km.UnlockKey(key)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_DoubleLock_DoubleUnlock(t *testing.T) {
|
||||
for _, km := range newKeyMutexes() {
|
||||
// Arrange
|
||||
key := "fakeid"
|
||||
callbackCh1stLock := make(chan interface{})
|
||||
callbackCh2ndLock := make(chan interface{})
|
||||
|
||||
// Act & Assert
|
||||
go lockAndCallback(km, key, callbackCh1stLock)
|
||||
verifyCallbackHappens(t, callbackCh1stLock)
|
||||
go lockAndCallback(km, key, callbackCh2ndLock)
|
||||
verifyCallbackDoesntHappens(t, callbackCh2ndLock)
|
||||
km.UnlockKey(key)
|
||||
verifyCallbackHappens(t, callbackCh2ndLock)
|
||||
km.UnlockKey(key)
|
||||
}
|
||||
}
|
||||
|
||||
func lockAndCallback(km KeyMutex, id string, callbackCh chan<- interface{}) {
|
||||
km.LockKey(id)
|
||||
callbackCh <- true
|
||||
}
|
||||
|
||||
func verifyCallbackHappens(t *testing.T, callbackCh <-chan interface{}) bool {
|
||||
select {
|
||||
case <-callbackCh:
|
||||
return true
|
||||
case <-time.After(callbackTimeout):
|
||||
t.Fatalf("Timed out waiting for callback.")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func verifyCallbackDoesntHappens(t *testing.T, callbackCh <-chan interface{}) bool {
|
||||
select {
|
||||
case <-callbackCh:
|
||||
t.Fatalf("Unexpected callback.")
|
||||
return false
|
||||
case <-time.After(callbackTimeout):
|
||||
return true
|
||||
}
|
||||
}
|
||||
13
vendor/k8s.io/utils/mount/OWNERS
generated
vendored
13
vendor/k8s.io/utils/mount/OWNERS
generated
vendored
@@ -1,13 +0,0 @@
|
||||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
reviewers:
|
||||
- jingxu97
|
||||
- saad-ali
|
||||
- jsafrane
|
||||
- msau42
|
||||
- andyzhangx
|
||||
approvers:
|
||||
- jingxu97
|
||||
- saad-ali
|
||||
- jsafrane
|
||||
|
||||
18
vendor/k8s.io/utils/mount/doc.go
generated
vendored
18
vendor/k8s.io/utils/mount/doc.go
generated
vendored
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
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/utils/mount"
|
||||
207
vendor/k8s.io/utils/mount/fake_mounter.go
generated
vendored
207
vendor/k8s.io/utils/mount/fake_mounter.go
generated
vendored
@@ -1,207 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
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: opts})
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
267
vendor/k8s.io/utils/mount/mount.go
generated
vendored
267
vendor/k8s.io/utils/mount/mount.go
generated
vendored
@@ -1,267 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
utilexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
const (
|
||||
// Default mount command if mounter path is not specified.
|
||||
defaultMountCommand = "mount"
|
||||
)
|
||||
|
||||
// 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.
|
||||
Mount(source string, target string, fstype string, options []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 {
|
||||
Device string
|
||||
Path string
|
||||
Type string
|
||||
Opts []string
|
||||
Freq int
|
||||
Pass int
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (mounter *SafeFormatAndMount) FormatAndMount(source string, target string, fstype string, options []string) error {
|
||||
return mounter.formatAndMount(source, target, fstype, options)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// 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"}
|
||||
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) {
|
||||
bindOpts = append(bindOpts, "_netdev")
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
switch option {
|
||||
case "bind":
|
||||
bind = true
|
||||
break
|
||||
case "remount":
|
||||
break
|
||||
default:
|
||||
bindRemountOpts = append(bindRemountOpts, option)
|
||||
}
|
||||
}
|
||||
|
||||
return bind, bindOpts, bindRemountOpts
|
||||
}
|
||||
|
||||
func checkForNetDev(options []string) bool {
|
||||
for _, option := range options {
|
||||
if option == "_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), "../")
|
||||
}
|
||||
99
vendor/k8s.io/utils/mount/mount_helper_common.go
generated
vendored
99
vendor/k8s.io/utils/mount/mount_helper_common.go
generated
vendored
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
if !corruptedMnt {
|
||||
var notMnt bool
|
||||
var err error
|
||||
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
|
||||
}
|
||||
|
||||
notMnt, mntErr := mounter.IsLikelyNotMountPoint(mountPath)
|
||||
if mntErr != nil {
|
||||
return mntErr
|
||||
}
|
||||
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
|
||||
}
|
||||
139
vendor/k8s.io/utils/mount/mount_helper_test.go
generated
vendored
139
vendor/k8s.io/utils/mount/mount_helper_test.go
generated
vendored
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDoCleanupMountPoint(t *testing.T) {
|
||||
|
||||
if runtime.GOOS == "darwin" {
|
||||
t.Skipf("not supported on GOOS=%s", runtime.GOOS)
|
||||
}
|
||||
|
||||
const testMount = "test-mount"
|
||||
const defaultPerm = 0750
|
||||
|
||||
tests := map[string]struct {
|
||||
corruptedMnt bool
|
||||
// Function that prepares the directory structure for the test under
|
||||
// the given base directory.
|
||||
// Returns a fake MountPoint, a fake error for the mount point,
|
||||
// and error if the prepare function encountered a fatal error.
|
||||
prepare func(base string) (MountPoint, error, error)
|
||||
expectErr bool
|
||||
}{
|
||||
"mount-ok": {
|
||||
prepare: func(base string) (MountPoint, error, error) {
|
||||
path := filepath.Join(base, testMount)
|
||||
if err := os.MkdirAll(path, defaultPerm); err != nil {
|
||||
return MountPoint{}, nil, err
|
||||
}
|
||||
return MountPoint{Device: "/dev/sdb", Path: path}, nil, nil
|
||||
},
|
||||
},
|
||||
"mount-corrupted": {
|
||||
prepare: func(base string) (MountPoint, error, error) {
|
||||
path := filepath.Join(base, testMount)
|
||||
if err := os.MkdirAll(path, defaultPerm); err != nil {
|
||||
return MountPoint{}, nil, err
|
||||
}
|
||||
return MountPoint{Device: "/dev/sdb", Path: path}, os.NewSyscallError("fake", syscall.ESTALE), nil
|
||||
},
|
||||
corruptedMnt: true,
|
||||
},
|
||||
"mount-err-not-corrupted": {
|
||||
prepare: func(base string) (MountPoint, error, error) {
|
||||
path := filepath.Join(base, testMount)
|
||||
if err := os.MkdirAll(path, defaultPerm); err != nil {
|
||||
return MountPoint{}, nil, err
|
||||
}
|
||||
return MountPoint{Device: "/dev/sdb", Path: path}, os.NewSyscallError("fake", syscall.ETIMEDOUT), nil
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tt := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "unmount-mount-point-test")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tmpdir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
if tt.prepare == nil {
|
||||
t.Fatalf("prepare function required")
|
||||
}
|
||||
|
||||
mountPoint, mountError, err := tt.prepare(tmpDir)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to prepare test: %v", err)
|
||||
}
|
||||
|
||||
fake := NewFakeMounter(
|
||||
[]MountPoint{mountPoint},
|
||||
)
|
||||
fake.MountCheckErrors = map[string]error{mountPoint.Path: mountError}
|
||||
|
||||
err = doCleanupMountPoint(mountPoint.Path, fake, true, tt.corruptedMnt)
|
||||
if tt.expectErr {
|
||||
if err == nil {
|
||||
t.Errorf("test %s failed, expected error, got none", name)
|
||||
}
|
||||
if err := validateDirExists(mountPoint.Path); err != nil {
|
||||
t.Errorf("test %s failed, mount path doesn't exist: %v", name, err)
|
||||
}
|
||||
}
|
||||
if !tt.expectErr {
|
||||
if err != nil {
|
||||
t.Errorf("test %s failed: %v", name, err)
|
||||
}
|
||||
if err := validateDirNotExists(mountPoint.Path); err != nil {
|
||||
t.Errorf("test %s failed, mount path still exists: %v", name, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func validateDirExists(dir string) error {
|
||||
_, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDirNotExists(dir string) error {
|
||||
_, err := ioutil.ReadDir(dir)
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("dir %q still exists", dir)
|
||||
}
|
||||
140
vendor/k8s.io/utils/mount/mount_helper_unix.go
generated
vendored
140
vendor/k8s.io/utils/mount/mount_helper_unix.go
generated
vendored
@@ -1,140 +0,0 @@
|
||||
// +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 {
|
||||
// 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
|
||||
// The value of `st_dev` for files on this filesystem.
|
||||
MajorMinor string
|
||||
// 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
|
||||
}
|
||||
info := MountInfo{
|
||||
ID: id,
|
||||
ParentID: parentID,
|
||||
MajorMinor: fields[2],
|
||||
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))
|
||||
}
|
||||
215
vendor/k8s.io/utils/mount/mount_helper_unix_test.go
generated
vendored
215
vendor/k8s.io/utils/mount/mount_helper_unix_test.go
generated
vendored
@@ -1,215 +0,0 @@
|
||||
// +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 (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func writeFile(content string) (string, string, error) {
|
||||
tempDir, err := ioutil.TempDir("", "mounter_shared_test")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
filename := filepath.Join(tempDir, "mountinfo")
|
||||
err = ioutil.WriteFile(filename, []byte(content), 0600)
|
||||
if err != nil {
|
||||
os.RemoveAll(tempDir)
|
||||
return "", "", err
|
||||
}
|
||||
return tempDir, filename, nil
|
||||
}
|
||||
|
||||
func TestParseMountInfo(t *testing.T) {
|
||||
info :=
|
||||
`62 0 253:0 / / rw,relatime shared:1 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
|
||||
78 62 0:41 / /tmp rw,nosuid,nodev shared:30 - tmpfs tmpfs rw,seclabel
|
||||
80 62 0:42 / /var/lib/nfs/rpc_pipefs rw,relatime shared:31 - rpc_pipefs sunrpc rw
|
||||
82 62 0:43 / /var/lib/foo rw,relatime shared:32 - tmpfs tmpfs rw
|
||||
83 63 0:44 / /var/lib/bar rw,relatime - tmpfs tmpfs rw
|
||||
227 62 253:0 /var/lib/docker/devicemapper /var/lib/docker/devicemapper rw,relatime - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
|
||||
224 62 253:0 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered
|
||||
76 17 8:1 / /mnt/stateful_partition rw,nosuid,nodev,noexec,relatime - ext4 /dev/sda1 rw,commit=30,data=ordered
|
||||
80 17 8:1 /var /var rw,nosuid,nodev,noexec,relatime shared:30 - ext4 /dev/sda1 rw,commit=30,data=ordered
|
||||
189 80 8:1 /var/lib/kubelet /var/lib/kubelet rw,relatime shared:30 - ext4 /dev/sda1 rw,commit=30,data=ordered
|
||||
818 77 8:40 / /var/lib/kubelet/pods/c25464af-e52e-11e7-ab4d-42010a800002/volumes/kubernetes.io~gce-pd/vol1 rw,relatime shared:290 - ext4 /dev/sdc rw,data=ordered
|
||||
819 78 8:48 / /var/lib/kubelet/pods/c25464af-e52e-11e7-ab4d-42010a800002/volumes/kubernetes.io~gce-pd/vol1 rw,relatime shared:290 - ext4 /dev/sdd rw,data=ordered
|
||||
900 100 8:48 /dir1 /var/lib/kubelet/pods/c25464af-e52e-11e7-ab4d-42010a800002/volume-subpaths/vol1/subpath1/0 rw,relatime shared:290 - ext4 /dev/sdd rw,data=ordered
|
||||
901 101 8:1 /dir1 /var/lib/kubelet/pods/c25464af-e52e-11e7-ab4d-42010a800002/volume-subpaths/vol1/subpath1/1 rw,relatime shared:290 - ext4 /dev/sdd rw,data=ordered
|
||||
902 102 8:1 /var/lib/kubelet/pods/d4076f24-e53a-11e7-ba15-42010a800002/volumes/kubernetes.io~empty-dir/vol1/dir1 /var/lib/kubelet/pods/d4076f24-e53a-11e7-ba15-42010a800002/volume-subpaths/vol1/subpath1/0 rw,relatime shared:30 - ext4 /dev/sda1 rw,commit=30,data=ordered
|
||||
903 103 8:1 /var/lib/kubelet/pods/d4076f24-e53a-11e7-ba15-42010a800002/volumes/kubernetes.io~empty-dir/vol2/dir1 /var/lib/kubelet/pods/d4076f24-e53a-11e7-ba15-42010a800002/volume-subpaths/vol1/subpath1/1 rw,relatime shared:30 - ext4 /dev/sda1 rw,commit=30,data=ordered
|
||||
178 25 253:0 /etc/bar /var/lib/kubelet/pods/12345/volume-subpaths/vol1/subpath1/0 rw,relatime shared:1 - ext4 /dev/sdb2 rw,errors=remount-ro,data=ordered
|
||||
698 186 0:41 /tmp1/dir1 /var/lib/kubelet/pods/41135147-e697-11e7-9342-42010a800002/volume-subpaths/vol1/subpath1/0 rw shared:26 - tmpfs tmpfs rw
|
||||
918 77 8:50 / /var/lib/kubelet/pods/2345/volumes/kubernetes.io~gce-pd/vol1 rw,relatime shared:290 - ext4 /dev/sdc rw,data=ordered
|
||||
919 78 8:58 / /var/lib/kubelet/pods/2345/volumes/kubernetes.io~gce-pd/vol1 rw,relatime shared:290 - ext4 /dev/sdd rw,data=ordered
|
||||
920 100 8:50 /dir1 /var/lib/kubelet/pods/2345/volume-subpaths/vol1/subpath1/0 rw,relatime shared:290 - ext4 /dev/sdc rw,data=ordered
|
||||
150 23 1:58 / /media/nfs_vol rw,relatime shared:89 - nfs4 172.18.4.223:/srv/nfs rw,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=172.18.4.223,local_lock=none,addr=172.18.4.223
|
||||
151 24 1:58 / /media/nfs_bindmount rw,relatime shared:89 - nfs4 172.18.4.223:/srv/nfs/foo rw,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=172.18.4.223,local_lock=none,addr=172.18.4.223
|
||||
134 23 0:58 / /var/lib/kubelet/pods/43219158-e5e1-11e7-a392-0e858b8eaf40/volumes/kubernetes.io~nfs/nfs1 rw,relatime shared:89 - nfs4 172.18.4.223:/srv/nfs rw,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=172.18.4.223,local_lock=none,addr=172.18.4.223
|
||||
187 23 0:58 / /var/lib/kubelet/pods/1fc5ea21-eff4-11e7-ac80-0e858b8eaf40/volumes/kubernetes.io~nfs/nfs2 rw,relatime shared:96 - nfs4 172.18.4.223:/srv/nfs2 rw,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=172.18.4.223,local_lock=none,addr=172.18.4.223
|
||||
188 24 0:58 / /var/lib/kubelet/pods/43219158-e5e1-11e7-a392-0e858b8eaf40/volume-subpaths/nfs1/subpath1/0 rw,relatime shared:89 - nfs4 172.18.4.223:/srv/nfs/foo rw,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=172.18.4.223,local_lock=none,addr=172.18.4.223
|
||||
347 60 0:71 / /var/lib/kubelet/pods/13195d46-f9fa-11e7-bbf1-5254007a695a/volumes/kubernetes.io~nfs/vol2 rw,relatime shared:170 - nfs 172.17.0.3:/exports/2 rw,vers=3,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=172.17.0.3,mountvers=3,mountport=20048,mountproto=udp,local_lock=none,addr=172.17.0.3
|
||||
222 24 253:0 /tmp/src /mnt/dst rw,relatime shared:1 - ext4 /dev/mapper/vagrant--vg-root rw,errors=remount-ro,data=ordered
|
||||
28 18 0:24 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:9 - tmpfs tmpfs ro,mode=755
|
||||
29 28 0:25 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
|
||||
31 28 0:27 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:13 - cgroup cgroup rw,cpuset
|
||||
32 28 0:28 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,cpu,cpuacct
|
||||
33 28 0:29 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,freezer
|
||||
34 28 0:30 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,net_cls,net_prio
|
||||
35 28 0:31 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,pids
|
||||
36 28 0:32 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,devices
|
||||
37 28 0:33 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,hugetlb
|
||||
38 28 0:34 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:20 - cgroup cgroup rw,blkio
|
||||
39 28 0:35 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:21 - cgroup cgroup rw,memory
|
||||
40 28 0:36 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:22 - cgroup cgroup rw,perf_event
|
||||
`
|
||||
tempDir, filename, err := writeFile(info)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot create temporary file: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
id int
|
||||
expectedInfo MountInfo
|
||||
}{
|
||||
{
|
||||
"simple bind mount",
|
||||
189,
|
||||
MountInfo{
|
||||
ID: 189,
|
||||
ParentID: 80,
|
||||
MajorMinor: "8:1",
|
||||
Root: "/var/lib/kubelet",
|
||||
Source: "/dev/sda1",
|
||||
MountPoint: "/var/lib/kubelet",
|
||||
OptionalFields: []string{"shared:30"},
|
||||
FsType: "ext4",
|
||||
MountOptions: []string{"rw", "relatime"},
|
||||
SuperOptions: []string{"rw", "commit=30", "data=ordered"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"bind mount a directory",
|
||||
222,
|
||||
MountInfo{
|
||||
ID: 222,
|
||||
ParentID: 24,
|
||||
MajorMinor: "253:0",
|
||||
Root: "/tmp/src",
|
||||
Source: "/dev/mapper/vagrant--vg-root",
|
||||
MountPoint: "/mnt/dst",
|
||||
OptionalFields: []string{"shared:1"},
|
||||
FsType: "ext4",
|
||||
MountOptions: []string{"rw", "relatime"},
|
||||
SuperOptions: []string{"rw", "errors=remount-ro", "data=ordered"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"more than one optional fields",
|
||||
224,
|
||||
MountInfo{
|
||||
ID: 224,
|
||||
ParentID: 62,
|
||||
MajorMinor: "253:0",
|
||||
Root: "/var/lib/docker/devicemapper/test/shared",
|
||||
Source: "/dev/mapper/ssd-root",
|
||||
MountPoint: "/var/lib/docker/devicemapper/test/shared",
|
||||
OptionalFields: []string{"master:1", "shared:44"},
|
||||
FsType: "ext4",
|
||||
MountOptions: []string{"rw", "relatime"},
|
||||
SuperOptions: []string{"rw", "seclabel", "data=ordered"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"cgroup-mountpoint",
|
||||
28,
|
||||
MountInfo{
|
||||
ID: 28,
|
||||
ParentID: 18,
|
||||
MajorMinor: "0:24",
|
||||
Root: "/",
|
||||
Source: "tmpfs",
|
||||
MountPoint: "/sys/fs/cgroup",
|
||||
OptionalFields: []string{"shared:9"},
|
||||
FsType: "tmpfs",
|
||||
MountOptions: []string{"ro", "nosuid", "nodev", "noexec"},
|
||||
SuperOptions: []string{"ro", "mode=755"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"cgroup-subsystem-systemd-mountpoint",
|
||||
29,
|
||||
MountInfo{
|
||||
ID: 29,
|
||||
ParentID: 28,
|
||||
MajorMinor: "0:25",
|
||||
Root: "/",
|
||||
Source: "cgroup",
|
||||
MountPoint: "/sys/fs/cgroup/systemd",
|
||||
OptionalFields: []string{"shared:10"},
|
||||
FsType: "cgroup",
|
||||
MountOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime"},
|
||||
SuperOptions: []string{"rw", "xattr", "release_agent=/lib/systemd/systemd-cgroups-agent", "name=systemd"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"cgroup-subsystem-cpuset-mountpoint",
|
||||
31,
|
||||
MountInfo{
|
||||
ID: 31,
|
||||
ParentID: 28,
|
||||
MajorMinor: "0:27",
|
||||
Root: "/",
|
||||
Source: "cgroup",
|
||||
MountPoint: "/sys/fs/cgroup/cpuset",
|
||||
OptionalFields: []string{"shared:13"},
|
||||
FsType: "cgroup",
|
||||
MountOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime"},
|
||||
SuperOptions: []string{"rw", "cpuset"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
infos, err := ParseMountInfo(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot parse %s: %s", filename, err)
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
found := false
|
||||
for _, info := range infos {
|
||||
if info.ID == test.id {
|
||||
found = true
|
||||
if !reflect.DeepEqual(info, test.expectedInfo) {
|
||||
t.Errorf("Test case %q:\n expected: %+v\n got: %+v", test.name, test.expectedInfo, info)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Test case %q: mountPoint %d not found", test.name, test.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
101
vendor/k8s.io/utils/mount/mount_helper_windows.go
generated
vendored
101
vendor/k8s.io/utils/mount/mount_helper_windows.go
generated
vendored
@@ -1,101 +0,0 @@
|
||||
// +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"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
65
vendor/k8s.io/utils/mount/mount_helper_windows_test.go
generated
vendored
65
vendor/k8s.io/utils/mount/mount_helper_windows_test.go
generated
vendored
@@ -1,65 +0,0 @@
|
||||
// +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 (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNormalizeWindowsPath(t *testing.T) {
|
||||
path := `/var/lib/kubelet/pods/146f8428-83e7-11e7-8dd4-000d3a31dac4/volumes/kubernetes.io~azure-disk`
|
||||
normalizedPath := NormalizeWindowsPath(path)
|
||||
if normalizedPath != `c:\var\lib\kubelet\pods\146f8428-83e7-11e7-8dd4-000d3a31dac4\volumes\kubernetes.io~azure-disk` {
|
||||
t.Errorf("normizeWindowsPath test failed, normalizedPath : %q", normalizedPath)
|
||||
}
|
||||
|
||||
path = `/var/lib/kubelet/pods/146f8428-83e7-11e7-8dd4-000d3a31dac4\volumes\kubernetes.io~azure-disk`
|
||||
normalizedPath = NormalizeWindowsPath(path)
|
||||
if normalizedPath != `c:\var\lib\kubelet\pods\146f8428-83e7-11e7-8dd4-000d3a31dac4\volumes\kubernetes.io~azure-disk` {
|
||||
t.Errorf("normizeWindowsPath test failed, normalizedPath : %q", normalizedPath)
|
||||
}
|
||||
|
||||
path = `/`
|
||||
normalizedPath = NormalizeWindowsPath(path)
|
||||
if normalizedPath != `c:\` {
|
||||
t.Errorf("normizeWindowsPath test failed, normalizedPath : %q", normalizedPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDiskNumber(t *testing.T) {
|
||||
diskNum := "0"
|
||||
if err := ValidateDiskNumber(diskNum); err != nil {
|
||||
t.Errorf("TestValidateDiskNumber test failed, disk number : %s", diskNum)
|
||||
}
|
||||
|
||||
diskNum = "99"
|
||||
if err := ValidateDiskNumber(diskNum); err != nil {
|
||||
t.Errorf("TestValidateDiskNumber test failed, disk number : %s", diskNum)
|
||||
}
|
||||
|
||||
diskNum = "ab"
|
||||
if err := ValidateDiskNumber(diskNum); err == nil {
|
||||
t.Errorf("TestValidateDiskNumber test failed, disk number : %s", diskNum)
|
||||
}
|
||||
|
||||
diskNum = "100"
|
||||
if err := ValidateDiskNumber(diskNum); err == nil {
|
||||
t.Errorf("TestValidateDiskNumber test failed, disk number : %s", diskNum)
|
||||
}
|
||||
}
|
||||
489
vendor/k8s.io/utils/mount/mount_linux.go
generated
vendored
489
vendor/k8s.io/utils/mount/mount_linux.go
generated
vendored
@@ -1,489 +0,0 @@
|
||||
// +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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"k8s.io/klog"
|
||||
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 {
|
||||
// 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 := MakeBindOpts(options)
|
||||
if bind {
|
||||
err := mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindRemountOpts)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
|
||||
// doMount runs the mount command. mounterPath is the path to mounter binary if containerized mounter is used.
|
||||
func (mounter *Mounter) doMount(mounterPath string, mountCmd string, source string, target string, fstype string, options []string) error {
|
||||
mountArgs := MakeMountArgs(source, target, fstype, options)
|
||||
if len(mounterPath) > 0 {
|
||||
mountArgs = append([]string{mountCmd}, mountArgs...)
|
||||
mountCmd = mounterPath
|
||||
}
|
||||
|
||||
if mounter.withSystemd {
|
||||
// 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 = AddSystemdScope("systemd-run", target, mountCmd, mountArgs)
|
||||
} 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.
|
||||
}
|
||||
|
||||
klog.V(4).Infof("Mounting cmd (%s) with arguments (%s)", mountCmd, mountArgs)
|
||||
command := exec.Command(mountCmd, mountArgs...)
|
||||
output, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
args := strings.Join(mountArgs, " ")
|
||||
klog.Errorf("Mount failed: %v\nMounting command: %s\nMounting arguments: %s\nOutput: %s\n", err, mountCmd, args, string(output))
|
||||
return fmt.Errorf("mount failed: %v\nMounting command: %s\nMounting arguments: %s\nOutput: %s",
|
||||
err, mountCmd, args, 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 failed with: %v", err)
|
||||
klog.V(4).Infof("systemd-run output: %s", string(output))
|
||||
return false
|
||||
}
|
||||
klog.V(2).Infof("Detected OS with systemd")
|
||||
return true
|
||||
}
|
||||
|
||||
// MakeMountArgs makes the arguments to the mount(8) command.
|
||||
// Implementation is shared with NsEnterMounter
|
||||
func MakeMountArgs(source, target, fstype string, options []string) []string {
|
||||
// Build mount command as follows:
|
||||
// mount [-t $fstype] [-o $options] [$source] $target
|
||||
mountArgs := []string{}
|
||||
if len(fstype) > 0 {
|
||||
mountArgs = append(mountArgs, "-t", fstype)
|
||||
}
|
||||
if len(options) > 0 {
|
||||
mountArgs = append(mountArgs, "-o", strings.Join(options, ","))
|
||||
}
|
||||
if len(source) > 0 {
|
||||
mountArgs = append(mountArgs, source)
|
||||
}
|
||||
mountArgs = append(mountArgs, target)
|
||||
|
||||
return mountArgs
|
||||
}
|
||||
|
||||
// AddSystemdScope adds "system-run --scope" to given command line
|
||||
// implementation is shared with NsEnterMounter
|
||||
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...)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// formatAndMount uses unix utils to format and mount the given disk
|
||||
func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
|
||||
readOnly := false
|
||||
for _, option := range options {
|
||||
if option == "ro" {
|
||||
readOnly = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
options = append(options, "defaults")
|
||||
|
||||
if !readOnly {
|
||||
// Run fsck on the disk to fix repairable issues, only do this for volumes requested as rw.
|
||||
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 fmt.Errorf("'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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to mount the disk
|
||||
klog.V(4).Infof("Attempting to mount disk: %s %s %s", fstype, source, target)
|
||||
mountErr := mounter.Interface.Mount(source, target, fstype, options)
|
||||
if mountErr != nil {
|
||||
// Mount failed. This indicates either that the disk is unformatted or
|
||||
// it contains an unexpected filesystem.
|
||||
existingFormat, err := mounter.GetDiskFormat(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingFormat == "" {
|
||||
if readOnly {
|
||||
// Don't attempt to format if mounting as readonly, return an error to reflect this.
|
||||
return errors.New("failed to mount unformatted volume as read only")
|
||||
}
|
||||
|
||||
// Disk is unformatted so format it.
|
||||
args := []string{source}
|
||||
// Use 'ext4' as the default
|
||||
if len(fstype) == 0 {
|
||||
fstype = "ext4"
|
||||
}
|
||||
|
||||
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)
|
||||
_, err := mounter.Exec.Command("mkfs."+fstype, args...).CombinedOutput()
|
||||
if err == nil {
|
||||
// the disk has been formatted successfully try to mount it again.
|
||||
klog.Infof("Disk successfully formatted (mkfs): %s - %s %s", fstype, source, target)
|
||||
return mounter.Interface.Mount(source, target, fstype, options)
|
||||
}
|
||||
klog.Errorf("format of disk %q failed: type:(%q) target:(%q) options:(%q)error:(%v)", source, fstype, target, options, err)
|
||||
return err
|
||||
}
|
||||
// Disk is already formatted and failed to mount
|
||||
if len(fstype) == 0 || fstype == existingFormat {
|
||||
// This is mount error
|
||||
return mountErr
|
||||
}
|
||||
// Block device is formatted with unexpected filesystem, let the user know
|
||||
return fmt.Errorf("failed to mount the volume as %q, it already contains %s. Mount error: %v", fstype, existingFormat, mountErr)
|
||||
}
|
||||
return mountErr
|
||||
}
|
||||
|
||||
// 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, err: %v", output, err)
|
||||
|
||||
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 {
|
||||
return nil, fmt.Errorf("wrong number of fields (expected %d, got %d): %s", expectedNumFieldsPerLine, len(fields), line)
|
||||
}
|
||||
|
||||
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 := ""
|
||||
majorMinor := ""
|
||||
|
||||
// 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))
|
||||
majorMinor = mis[i].MajorMinor
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if rootPath == "" || majorMinor == "" {
|
||||
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].MajorMinor == majorMinor {
|
||||
refs = append(refs, mis[i].MountPoint)
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
439
vendor/k8s.io/utils/mount/mount_linux_test.go
generated
vendored
439
vendor/k8s.io/utils/mount/mount_linux_test.go
generated
vendored
@@ -1,439 +0,0 @@
|
||||
// +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 (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReadProcMountsFrom(t *testing.T) {
|
||||
successCase :=
|
||||
`/dev/0 /path/to/0 type0 flags 0 0
|
||||
/dev/1 /path/to/1 type1 flags 1 1
|
||||
/dev/2 /path/to/2 type2 flags,1,2=3 2 2
|
||||
`
|
||||
// NOTE: readProcMountsFrom has been updated to using fnv.New32a()
|
||||
mounts, err := parseProcMounts([]byte(successCase))
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if len(mounts) != 3 {
|
||||
t.Fatalf("expected 3 mounts, got %d", len(mounts))
|
||||
}
|
||||
mp := MountPoint{"/dev/0", "/path/to/0", "type0", []string{"flags"}, 0, 0}
|
||||
if !mountPointsEqual(&mounts[0], &mp) {
|
||||
t.Errorf("got unexpected MountPoint[0]: %#v", mounts[0])
|
||||
}
|
||||
mp = MountPoint{"/dev/1", "/path/to/1", "type1", []string{"flags"}, 1, 1}
|
||||
if !mountPointsEqual(&mounts[1], &mp) {
|
||||
t.Errorf("got unexpected MountPoint[1]: %#v", mounts[1])
|
||||
}
|
||||
mp = MountPoint{"/dev/2", "/path/to/2", "type2", []string{"flags", "1", "2=3"}, 2, 2}
|
||||
if !mountPointsEqual(&mounts[2], &mp) {
|
||||
t.Errorf("got unexpected MountPoint[2]: %#v", mounts[2])
|
||||
}
|
||||
|
||||
errorCases := []string{
|
||||
"/dev/0 /path/to/mount\n",
|
||||
"/dev/1 /path/to/mount type flags a 0\n",
|
||||
"/dev/2 /path/to/mount type flags 0 b\n",
|
||||
}
|
||||
for _, ec := range errorCases {
|
||||
_, err := parseProcMounts([]byte(ec))
|
||||
if err == nil {
|
||||
t.Errorf("expected error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mountPointsEqual(a, b *MountPoint) bool {
|
||||
if a.Device != b.Device || a.Path != b.Path || a.Type != b.Type || !reflect.DeepEqual(a.Opts, b.Opts) || a.Pass != b.Pass || a.Freq != b.Freq {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestGetMountRefs(t *testing.T) {
|
||||
fm := NewFakeMounter(
|
||||
[]MountPoint{
|
||||
{Device: "/dev/sdb", Path: "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd"},
|
||||
{Device: "/dev/sdb", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd-in-pod"},
|
||||
{Device: "/dev/sdc", Path: "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd2"},
|
||||
{Device: "/dev/sdc", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod1"},
|
||||
{Device: "/dev/sdc", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod2"},
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
mountPath string
|
||||
expectedRefs []string
|
||||
}{
|
||||
{
|
||||
"/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd-in-pod",
|
||||
[]string{
|
||||
"/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd",
|
||||
},
|
||||
},
|
||||
{
|
||||
"/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod1",
|
||||
[]string{
|
||||
"/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod2",
|
||||
"/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd2",
|
||||
},
|
||||
},
|
||||
{
|
||||
"/var/fake/directory/that/doesnt/exist",
|
||||
[]string{},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
if refs, err := fm.GetMountRefs(test.mountPath); err != nil || !setEquivalent(test.expectedRefs, refs) {
|
||||
t.Errorf("%d. getMountRefs(%q) = %v, %v; expected %v, nil", i, test.mountPath, refs, err, test.expectedRefs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setEquivalent(set1, set2 []string) bool {
|
||||
map1 := make(map[string]bool)
|
||||
map2 := make(map[string]bool)
|
||||
for _, s := range set1 {
|
||||
map1[s] = true
|
||||
}
|
||||
for _, s := range set2 {
|
||||
map2[s] = true
|
||||
}
|
||||
|
||||
for s := range map1 {
|
||||
if !map2[s] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for s := range map2 {
|
||||
if !map1[s] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestGetDeviceNameFromMount(t *testing.T) {
|
||||
fm := NewFakeMounter(
|
||||
[]MountPoint{
|
||||
{Device: "/dev/disk/by-path/prefix-lun-1",
|
||||
Path: "/mnt/111"},
|
||||
{Device: "/dev/disk/by-path/prefix-lun-1",
|
||||
Path: "/mnt/222"},
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
mountPath string
|
||||
expectedDevice string
|
||||
expectedRefs int
|
||||
}{
|
||||
{
|
||||
"/mnt/222",
|
||||
"/dev/disk/by-path/prefix-lun-1",
|
||||
2,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
if device, refs, err := GetDeviceNameFromMount(fm, test.mountPath); err != nil || test.expectedRefs != refs || test.expectedDevice != device {
|
||||
t.Errorf("%d. GetDeviceNameFromMount(%s) = (%s, %d), %v; expected (%s,%d), nil", i, test.mountPath, device, refs, err, test.expectedDevice, test.expectedRefs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMountRefsByDev(t *testing.T) {
|
||||
fm := NewFakeMounter(
|
||||
[]MountPoint{
|
||||
{Device: "/dev/sdb", Path: "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd"},
|
||||
{Device: "/dev/sdb", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd-in-pod"},
|
||||
{Device: "/dev/sdc", Path: "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd2"},
|
||||
{Device: "/dev/sdc", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod1"},
|
||||
{Device: "/dev/sdc", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod2"},
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
mountPath string
|
||||
expectedRefs []string
|
||||
}{
|
||||
{
|
||||
"/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd",
|
||||
[]string{
|
||||
"/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd-in-pod",
|
||||
},
|
||||
},
|
||||
{
|
||||
"/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd2",
|
||||
[]string{
|
||||
"/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod1",
|
||||
"/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
|
||||
if refs, err := getMountRefsByDev(fm, test.mountPath); err != nil || !setEquivalent(test.expectedRefs, refs) {
|
||||
t.Errorf("%d. getMountRefsByDev(%q) = %v, %v; expected %v, nil", i, test.mountPath, refs, err, test.expectedRefs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathWithinBase(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fullPath string
|
||||
basePath string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "good subpath",
|
||||
fullPath: "/a/b/c",
|
||||
basePath: "/a",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "good subpath 2",
|
||||
fullPath: "/a/b/c",
|
||||
basePath: "/a/b",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "good subpath end slash",
|
||||
fullPath: "/a/b/c/",
|
||||
basePath: "/a/b",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "good subpath backticks",
|
||||
fullPath: "/a/b/../c",
|
||||
basePath: "/a",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "good subpath equal",
|
||||
fullPath: "/a/b/c",
|
||||
basePath: "/a/b/c",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "good subpath equal 2",
|
||||
fullPath: "/a/b/c/",
|
||||
basePath: "/a/b/c",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "good subpath root",
|
||||
fullPath: "/a",
|
||||
basePath: "/",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "bad subpath parent",
|
||||
fullPath: "/a/b/c",
|
||||
basePath: "/a/b/c/d",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "bad subpath outside",
|
||||
fullPath: "/b/c",
|
||||
basePath: "/a/b/c",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "bad subpath prefix",
|
||||
fullPath: "/a/b/cd",
|
||||
basePath: "/a/b/c",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "bad subpath backticks",
|
||||
fullPath: "/a/../b",
|
||||
basePath: "/a",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "configmap subpath",
|
||||
fullPath: "/var/lib/kubelet/pods/uuid/volumes/kubernetes.io~configmap/config/..timestamp/file.txt",
|
||||
basePath: "/var/lib/kubelet/pods/uuid/volumes/kubernetes.io~configmap/config",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if PathWithinBase(test.fullPath, test.basePath) != test.expected {
|
||||
t.Errorf("test %q failed: expected %v", test.name, test.expected)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchMountPoints(t *testing.T) {
|
||||
base := `
|
||||
19 25 0:18 / /sys rw,nosuid,nodev,noexec,relatime shared:7 - sysfs sysfs rw
|
||||
20 25 0:4 / /proc rw,nosuid,nodev,noexec,relatime shared:12 - proc proc rw
|
||||
21 25 0:6 / /dev rw,nosuid,relatime shared:2 - devtmpfs udev rw,size=4058156k,nr_inodes=1014539,mode=755
|
||||
22 21 0:14 / /dev/pts rw,nosuid,noexec,relatime shared:3 - devpts devpts rw,gid=5,mode=620,ptmxmode=000
|
||||
23 25 0:19 / /run rw,nosuid,noexec,relatime shared:5 - tmpfs tmpfs rw,size=815692k,mode=755
|
||||
25 0 252:0 / / rw,relatime shared:1 - ext4 /dev/mapper/ubuntu--vg-root rw,errors=remount-ro,data=ordered
|
||||
26 19 0:12 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:8 - securityfs securityfs rw
|
||||
27 21 0:21 / /dev/shm rw,nosuid,nodev shared:4 - tmpfs tmpfs rw
|
||||
28 23 0:22 / /run/lock rw,nosuid,nodev,noexec,relatime shared:6 - tmpfs tmpfs rw,size=5120k
|
||||
29 19 0:23 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:9 - tmpfs tmpfs ro,mode=755
|
||||
30 29 0:24 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
|
||||
31 19 0:25 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime shared:11 - pstore pstore rw
|
||||
32 29 0:26 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:13 - cgroup cgroup rw,devices
|
||||
33 29 0:27 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,freezer
|
||||
34 29 0:28 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,pids
|
||||
35 29 0:29 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,blkio
|
||||
36 29 0:30 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,memory
|
||||
37 29 0:31 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,perf_event
|
||||
38 29 0:32 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,hugetlb
|
||||
39 29 0:33 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:20 - cgroup cgroup rw,cpu,cpuacct
|
||||
40 29 0:34 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:21 - cgroup cgroup rw,cpuset
|
||||
41 29 0:35 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:22 - cgroup cgroup rw,net_cls,net_prio
|
||||
58 25 7:1 / /mnt/disks/blkvol1 rw,relatime shared:38 - ext4 /dev/loop1 rw,data=ordere
|
||||
`
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
source string
|
||||
mountInfos string
|
||||
expectedRefs []string
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
"dir",
|
||||
"/mnt/disks/vol1",
|
||||
base,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"dir-used",
|
||||
"/mnt/disks/vol1",
|
||||
base + `
|
||||
56 25 252:0 /mnt/disks/vol1 /var/lib/kubelet/pods/1890aef5-5a60-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test rw,relatime shared:1 - ext4 /dev/mapper/ubuntu--vg-root rw,errors=remount-ro,data=ordered
|
||||
57 25 0:45 / /mnt/disks/vol rw,relatime shared:36 - tmpfs tmpfs rw
|
||||
`,
|
||||
[]string{"/var/lib/kubelet/pods/1890aef5-5a60-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"tmpfs-vol",
|
||||
"/mnt/disks/vol1",
|
||||
base + `120 25 0:76 / /mnt/disks/vol1 rw,relatime shared:41 - tmpfs vol1 rw,size=10000k
|
||||
`,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"tmpfs-vol-used-by-two-pods",
|
||||
"/mnt/disks/vol1",
|
||||
base + `120 25 0:76 / /mnt/disks/vol1 rw,relatime shared:41 - tmpfs vol1 rw,size=10000k
|
||||
196 25 0:76 / /var/lib/kubelet/pods/ade3ac21-5a5b-11e8-8559-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-8f263585 rw,relatime shared:41 - tmpfs vol1 rw,size=10000k
|
||||
228 25 0:76 / /var/lib/kubelet/pods/ac60532d-5a5b-11e8-8559-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-8f263585 rw,relatime shared:41 - tmpfs vol1 rw,size=10000k
|
||||
`,
|
||||
[]string{
|
||||
"/var/lib/kubelet/pods/ade3ac21-5a5b-11e8-8559-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-8f263585",
|
||||
"/var/lib/kubelet/pods/ac60532d-5a5b-11e8-8559-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-8f263585",
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"tmpfs-subdir-used-indirectly-via-bindmount-dir-by-one-pod",
|
||||
"/mnt/vol1/foo",
|
||||
base + `177 25 0:46 / /mnt/data rw,relatime shared:37 - tmpfs data rw
|
||||
190 25 0:46 /vol1 /mnt/vol1 rw,relatime shared:37 - tmpfs data rw
|
||||
191 25 0:46 /vol2 /mnt/vol2 rw,relatime shared:37 - tmpfs data rw
|
||||
62 25 0:46 /vol1/foo /var/lib/kubelet/pods/e25f2f01-5b06-11e8-8694-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test rw,relatime shared:37 - tmpfs data rw
|
||||
`,
|
||||
[]string{"/var/lib/kubelet/pods/e25f2f01-5b06-11e8-8694-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"dir-bindmounted",
|
||||
"/mnt/disks/vol2",
|
||||
base + `342 25 252:0 /mnt/disks/vol2 /mnt/disks/vol2 rw,relatime shared:1 - ext4 /dev/mapper/ubuntu--vg-root rw,errors=remount-ro,data=ordered
|
||||
`,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"dir-bindmounted-used-by-one-pod",
|
||||
"/mnt/disks/vol2",
|
||||
base + `342 25 252:0 /mnt/disks/vol2 /mnt/disks/vol2 rw,relatime shared:1 - ext4 /dev/mapper/ubuntu--vg-root rw,errors=remount-ro,data=ordered
|
||||
77 25 252:0 /mnt/disks/vol2 /var/lib/kubelet/pods/f30dc360-5a5d-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-1fb30a1c rw,relatime shared:1 - ext4 /dev/mapper/ubuntu--vg-root rw,errors=remount-ro,data=ordered
|
||||
`,
|
||||
[]string{"/var/lib/kubelet/pods/f30dc360-5a5d-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-1fb30a1c"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"blockfs",
|
||||
"/mnt/disks/blkvol1",
|
||||
base + `58 25 7:1 / /mnt/disks/blkvol1 rw,relatime shared:38 - ext4 /dev/loop1 rw,data=ordered
|
||||
`,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"blockfs-used-by-one-pod",
|
||||
"/mnt/disks/blkvol1",
|
||||
base + `58 25 7:1 / /mnt/disks/blkvol1 rw,relatime shared:38 - ext4 /dev/loop1 rw,data=ordered
|
||||
62 25 7:1 / /var/lib/kubelet/pods/f19fe4e2-5a63-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test rw,relatime shared:38 - ext4 /dev/loop1 rw,data=ordered
|
||||
`,
|
||||
[]string{"/var/lib/kubelet/pods/f19fe4e2-5a63-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"blockfs-used-by-two-pods",
|
||||
"/mnt/disks/blkvol1",
|
||||
base + `58 25 7:1 / /mnt/disks/blkvol1 rw,relatime shared:38 - ext4 /dev/loop1 rw,data=ordered
|
||||
62 25 7:1 / /var/lib/kubelet/pods/f19fe4e2-5a63-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test rw,relatime shared:38 - ext4 /dev/loop1 rw,data=ordered
|
||||
95 25 7:1 / /var/lib/kubelet/pods/4854a48b-5a64-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test rw,relatime shared:38 - ext4 /dev/loop1 rw,data=ordered
|
||||
`,
|
||||
[]string{"/var/lib/kubelet/pods/f19fe4e2-5a63-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test",
|
||||
"/var/lib/kubelet/pods/4854a48b-5a64-11e8-962f-000c29bb0377/volumes/kubernetes.io~local-volume/local-pv-test"},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
tmpFile, err := ioutil.TempFile("", "test-get-filetype")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(tmpFile.Name())
|
||||
defer tmpFile.Close()
|
||||
for _, v := range testcases {
|
||||
tmpFile.Truncate(0)
|
||||
tmpFile.Seek(0, 0)
|
||||
tmpFile.WriteString(v.mountInfos)
|
||||
tmpFile.Sync()
|
||||
refs, err := SearchMountPoints(v.source, tmpFile.Name())
|
||||
if !reflect.DeepEqual(refs, v.expectedRefs) {
|
||||
t.Errorf("test %q: expected Refs: %#v, got %#v", v.name, v.expectedRefs, refs)
|
||||
}
|
||||
if !reflect.DeepEqual(err, v.expectedErr) {
|
||||
t.Errorf("test %q: expected err: %v, got %v", v.name, v.expectedErr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
60
vendor/k8s.io/utils/mount/mount_test.go
generated
vendored
60
vendor/k8s.io/utils/mount/mount_test.go
generated
vendored
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMakeBindOpts(t *testing.T) {
|
||||
tests := []struct {
|
||||
mountOption []string
|
||||
isBind bool
|
||||
expectedBindOpts []string
|
||||
expectedRemountOpts []string
|
||||
}{
|
||||
{
|
||||
[]string{"vers=2", "ro", "_netdev"},
|
||||
false,
|
||||
[]string{},
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
|
||||
[]string{"bind", "vers=2", "ro", "_netdev"},
|
||||
true,
|
||||
[]string{"bind", "_netdev"},
|
||||
[]string{"bind", "remount", "vers=2", "ro", "_netdev"},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
bind, bindOpts, bindRemountOpts := MakeBindOpts(test.mountOption)
|
||||
if bind != test.isBind {
|
||||
t.Errorf("Expected bind to be %v but got %v", test.isBind, bind)
|
||||
}
|
||||
if test.isBind {
|
||||
if !reflect.DeepEqual(test.expectedBindOpts, bindOpts) {
|
||||
t.Errorf("Expected bind mount options to be %+v got %+v", test.expectedBindOpts, bindOpts)
|
||||
}
|
||||
if !reflect.DeepEqual(test.expectedRemountOpts, bindRemountOpts) {
|
||||
t.Errorf("Expected remount options to be %+v got %+v", test.expectedRemountOpts, bindRemountOpts)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
72
vendor/k8s.io/utils/mount/mount_unsupported.go
generated
vendored
72
vendor/k8s.io/utils/mount/mount_unsupported.go
generated
vendored
@@ -1,72 +0,0 @@
|
||||
// +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
|
||||
}
|
||||
|
||||
// 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) formatAndMount(source string, target string, fstype string, options []string) error {
|
||||
return mounter.Interface.Mount(source, target, fstype, options)
|
||||
}
|
||||
|
||||
func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) {
|
||||
return true, errUnsupported
|
||||
}
|
||||
280
vendor/k8s.io/utils/mount/mount_windows.go
generated
vendored
280
vendor/k8s.io/utils/mount/mount_windows.go
generated
vendored
@@ -1,280 +0,0 @@
|
||||
// +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"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
"k8s.io/utils/keymutex"
|
||||
utilpath "k8s.io/utils/path"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
target = NormalizeWindowsPath(target)
|
||||
|
||||
if source == "tmpfs" {
|
||||
klog.V(3).Infof("mounting source (%q), target (%q), with options (%q)", source, target, options)
|
||||
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",
|
||||
options, source, target, fstype)
|
||||
bindSource := source
|
||||
|
||||
// tell it's going to mount azure disk or azure file according to options
|
||||
if bind, _, _ := MakeBindOpts(options); bind {
|
||||
// mount azure disk
|
||||
bindSource = NormalizeWindowsPath(source)
|
||||
} else {
|
||||
if len(options) < 2 {
|
||||
klog.Warningf("mount options(%q) command number(%d) less than 2, source:%q, target:%q, skip mounting",
|
||||
options, len(options), source, target)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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, options)
|
||||
}
|
||||
|
||||
// lock smb mount for the same source
|
||||
getSMBMountMutex.LockKey(source)
|
||||
defer getSMBMountMutex.UnlockKey(source)
|
||||
|
||||
if output, err := newSMBMapping(options[0], options[1], source); err != nil {
|
||||
if isSMBMappingExist(source) {
|
||||
klog.V(2).Infof("SMB Mapping(%s) already exists, now begin to remove and remount", source)
|
||||
if output, err := removeSMBMapping(source); err != nil {
|
||||
return fmt.Errorf("Remove-SmbGlobalMapping failed: %v, output: %q", err, output)
|
||||
}
|
||||
if output, err := newSMBMapping(options[0], options[1], source); err != nil {
|
||||
return fmt.Errorf("New-SmbGlobalMapping remount failed: %v, output: %q", err, output)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("New-SmbGlobalMapping failed: %v, output: %q", err, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if output, err := exec.Command("cmd", "/c", "mklink", "/D", target, bindSource).CombinedOutput(); err != nil {
|
||||
klog.Errorf("mklink failed: %v, source(%q) target(%q) output: %q", err, bindSource, target, string(output))
|
||||
return err
|
||||
}
|
||||
|
||||
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, password, 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
|
||||
}
|
||||
|
||||
// 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 current file is a symlink, then it is a mountpoint.
|
||||
if stat.Mode()&os.ModeSymlink != 0 {
|
||||
target, err := os.Readlink(file)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("readlink error: %v", err)
|
||||
}
|
||||
exists, err := utilpath.Exists(utilpath.CheckFollowSymlink, target)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
return !exists, nil
|
||||
}
|
||||
|
||||
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) formatAndMount(source string, target string, fstype string, options []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 -AssignDriveLetter -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)
|
||||
|
||||
driveLetter, err := getDriveLetterByDiskNumber(source, mounter.Exec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
driverPath := driveLetter + ":"
|
||||
target = NormalizeWindowsPath(target)
|
||||
klog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, driverPath, target)
|
||||
if output, err := mounter.Exec.Command("cmd", "/c", "mklink", "/D", target, driverPath).CombinedOutput(); err != nil {
|
||||
klog.Errorf("mklink failed: %v, output: %q", err, string(output))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get drive letter according to windows disk number
|
||||
func getDriveLetterByDiskNumber(diskNum string, exec utilexec.Interface) (string, error) {
|
||||
cmd := fmt.Sprintf("(Get-Partition -DiskNumber %s).DriveLetter", diskNum)
|
||||
output, err := exec.Command("powershell", "/c", cmd).CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("azureMount: Get Drive Letter failed: %v, output: %q", err, string(output))
|
||||
}
|
||||
if len(string(output)) < 1 {
|
||||
return "", fmt.Errorf("azureMount: Get Drive Letter failed, output is empty")
|
||||
}
|
||||
return string(output)[:1], 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
|
||||
}
|
||||
333
vendor/k8s.io/utils/mount/mount_windows_test.go
generated
vendored
333
vendor/k8s.io/utils/mount/mount_windows_test.go
generated
vendored
@@ -1,333 +0,0 @@
|
||||
// +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"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/utils/exec/testing"
|
||||
)
|
||||
|
||||
func makeLink(link, target string) error {
|
||||
if output, err := exec.Command("cmd", "/c", "mklink", "/D", link, target).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("mklink failed: %v, link(%q) target(%q) output: %q", err, link, target, string(output))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeLink(link string) error {
|
||||
if output, err := exec.Command("cmd", "/c", "rmdir", link).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("rmdir failed: %v, output: %q", err, string(output))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setEquivalent(set1, set2 []string) bool {
|
||||
map1 := make(map[string]bool)
|
||||
map2 := make(map[string]bool)
|
||||
for _, s := range set1 {
|
||||
map1[s] = true
|
||||
}
|
||||
for _, s := range set2 {
|
||||
map2[s] = true
|
||||
}
|
||||
|
||||
for s := range map1 {
|
||||
if !map2[s] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for s := range map2 {
|
||||
if !map1[s] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// this func must run in admin mode, otherwise it will fail
|
||||
func TestGetMountRefs(t *testing.T) {
|
||||
tests := []struct {
|
||||
mountPath string
|
||||
expectedRefs []string
|
||||
}{
|
||||
{
|
||||
mountPath: `c:\windows`,
|
||||
expectedRefs: []string{`c:\windows`},
|
||||
},
|
||||
{
|
||||
mountPath: `c:\doesnotexist`,
|
||||
expectedRefs: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
mounter := Mounter{"fake/path"}
|
||||
|
||||
for _, test := range tests {
|
||||
if refs, err := mounter.GetMountRefs(test.mountPath); err != nil || !setEquivalent(test.expectedRefs, refs) {
|
||||
t.Errorf("getMountRefs(%q) = %v, error: %v; expected %v", test.mountPath, refs, err, test.expectedRefs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathWithinBase(t *testing.T) {
|
||||
tests := []struct {
|
||||
fullPath string
|
||||
basePath string
|
||||
expectedResult bool
|
||||
}{
|
||||
{
|
||||
fullPath: `c:\tmp\a\b\c`,
|
||||
basePath: `c:\tmp`,
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
fullPath: `c:\tmp1`,
|
||||
basePath: `c:\tmp2`,
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
fullPath: `c:\tmp`,
|
||||
basePath: `c:\tmp`,
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
fullPath: `c:\tmp`,
|
||||
basePath: `c:\tmp\a\b\c`,
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
fullPath: `c:\kubelet\pods\uuid\volumes\kubernetes.io~configmap\config\..timestamp\file.txt`,
|
||||
basePath: `c:\kubelet\pods\uuid\volumes\kubernetes.io~configmap\config`,
|
||||
expectedResult: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
result := PathWithinBase(test.fullPath, test.basePath)
|
||||
assert.Equal(t, result, test.expectedResult, "Expect result not equal with PathWithinBase(%s, %s) return: %q, expected: %q",
|
||||
test.fullPath, test.basePath, result, test.expectedResult)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsLikelyNotMountPoint(t *testing.T) {
|
||||
mounter := Mounter{"fake/path"}
|
||||
|
||||
tests := []struct {
|
||||
fileName string
|
||||
targetLinkName string
|
||||
setUp func(base, fileName, targetLinkName string) error
|
||||
expectedResult bool
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
"Dir",
|
||||
"",
|
||||
func(base, fileName, targetLinkName string) error {
|
||||
return os.Mkdir(filepath.Join(base, fileName), 0750)
|
||||
},
|
||||
true,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"InvalidDir",
|
||||
"",
|
||||
func(base, fileName, targetLinkName string) error {
|
||||
return nil
|
||||
},
|
||||
true,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ValidSymLink",
|
||||
"targetSymLink",
|
||||
func(base, fileName, targetLinkName string) error {
|
||||
targeLinkPath := filepath.Join(base, targetLinkName)
|
||||
if err := os.Mkdir(targeLinkPath, 0750); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filePath := filepath.Join(base, fileName)
|
||||
if err := makeLink(filePath, targeLinkPath); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"InvalidSymLink",
|
||||
"targetSymLink2",
|
||||
func(base, fileName, targetLinkName string) error {
|
||||
targeLinkPath := filepath.Join(base, targetLinkName)
|
||||
if err := os.Mkdir(targeLinkPath, 0750); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filePath := filepath.Join(base, fileName)
|
||||
if err := makeLink(filePath, targeLinkPath); err != nil {
|
||||
return err
|
||||
}
|
||||
return removeLink(targeLinkPath)
|
||||
},
|
||||
true,
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
base, err := ioutil.TempDir("", test.fileName)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
defer os.RemoveAll(base)
|
||||
|
||||
if err := test.setUp(base, test.fileName, test.targetLinkName); err != nil {
|
||||
t.Fatalf("unexpected error in setUp(%s, %s): %v", test.fileName, test.targetLinkName, err)
|
||||
}
|
||||
|
||||
filePath := filepath.Join(base, test.fileName)
|
||||
result, err := mounter.IsLikelyNotMountPoint(filePath)
|
||||
assert.Equal(t, result, test.expectedResult, "Expect result not equal with IsLikelyNotMountPoint(%s) return: %q, expected: %q",
|
||||
filePath, result, test.expectedResult)
|
||||
|
||||
if test.expectError {
|
||||
assert.NotNil(t, err, "Expect error during IsLikelyNotMountPoint(%s)", filePath)
|
||||
} else {
|
||||
assert.Nil(t, err, "Expect error is nil during IsLikelyNotMountPoint(%s)", filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatAndMount(t *testing.T) {
|
||||
tests := []struct {
|
||||
device string
|
||||
target string
|
||||
fstype string
|
||||
execScripts []ExecArgs
|
||||
mountOptions []string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
device: "0",
|
||||
target: "disk",
|
||||
fstype: "NTFS",
|
||||
execScripts: []ExecArgs{
|
||||
{"powershell", []string{"/c", "Get-Disk", "-Number"}, "0", nil},
|
||||
{"powershell", []string{"/c", "Get-Partition", "-DiskNumber"}, "0", nil},
|
||||
{"cmd", []string{"/c", "mklink", "/D"}, "", nil},
|
||||
},
|
||||
mountOptions: []string{},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
device: "0",
|
||||
target: "disk",
|
||||
fstype: "",
|
||||
execScripts: []ExecArgs{
|
||||
{"powershell", []string{"/c", "Get-Disk", "-Number"}, "0", nil},
|
||||
{"powershell", []string{"/c", "Get-Partition", "-DiskNumber"}, "0", nil},
|
||||
{"cmd", []string{"/c", "mklink", "/D"}, "", nil},
|
||||
},
|
||||
mountOptions: []string{},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
device: "invalidDevice",
|
||||
target: "disk",
|
||||
fstype: "NTFS",
|
||||
mountOptions: []string{},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
fakeMounter := ErrorMounter{NewFakeMounter(nil), 0, nil}
|
||||
fakeExec := &testingexec.FakeExec{}
|
||||
for _, script := range test.execScripts {
|
||||
fakeCmd := &testingexec.FakeCmd{}
|
||||
cmdAction := makeFakeCmd(fakeCmd, script.command, script.args...)
|
||||
outputAction := makeFakeOutput(script.output, script.err)
|
||||
fakeCmd.CombinedOutputScript = append(fakeCmd.CombinedOutputScript, outputAction)
|
||||
fakeExec.CommandScript = append(fakeExec.CommandScript, cmdAction)
|
||||
}
|
||||
mounter := SafeFormatAndMount{
|
||||
Interface: &fakeMounter,
|
||||
Exec: fakeExec,
|
||||
}
|
||||
base, err := ioutil.TempDir("", test.device)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
defer os.RemoveAll(base)
|
||||
|
||||
target := filepath.Join(base, test.target)
|
||||
err = mounter.FormatAndMount(test.device, target, test.fstype, test.mountOptions)
|
||||
if test.expectError {
|
||||
assert.NotNil(t, err, "Expect error during FormatAndMount(%s, %s, %s, %v)", test.device, test.target, test.fstype, test.mountOptions)
|
||||
} else {
|
||||
assert.Nil(t, err, "Expect error is nil during FormatAndMount(%s, %s, %s, %v)", test.device, test.target, test.fstype, test.mountOptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewSMBMapping(t *testing.T) {
|
||||
tests := []struct {
|
||||
username string
|
||||
password string
|
||||
remotepath string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
"",
|
||||
"password",
|
||||
`\\remotepath`,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"username",
|
||||
"",
|
||||
`\\remotepath`,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"username",
|
||||
"password",
|
||||
"",
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
_, err := newSMBMapping(test.username, test.password, test.remotepath)
|
||||
if test.expectError {
|
||||
assert.NotNil(t, err, "Expect error during newSMBMapping(%s, %s, %s, %v)", test.username, test.password, test.remotepath)
|
||||
} else {
|
||||
assert.Nil(t, err, "Expect error is nil during newSMBMapping(%s, %s, %s, %v)", test.username, test.password, test.remotepath)
|
||||
}
|
||||
}
|
||||
}
|
||||
248
vendor/k8s.io/utils/mount/safe_format_and_mount_test.go
generated
vendored
248
vendor/k8s.io/utils/mount/safe_format_and_mount_test.go
generated
vendored
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/utils/exec"
|
||||
"k8s.io/utils/exec/testing"
|
||||
)
|
||||
|
||||
type ErrorMounter struct {
|
||||
*FakeMounter
|
||||
errIndex int
|
||||
err []error
|
||||
}
|
||||
|
||||
func (mounter *ErrorMounter) Mount(source string, target string, fstype string, options []string) error {
|
||||
i := mounter.errIndex
|
||||
mounter.errIndex++
|
||||
if mounter.err != nil && mounter.err[i] != nil {
|
||||
return mounter.err[i]
|
||||
}
|
||||
return mounter.FakeMounter.Mount(source, target, fstype, options)
|
||||
}
|
||||
|
||||
type ExecArgs struct {
|
||||
command string
|
||||
args []string
|
||||
output string
|
||||
err error
|
||||
}
|
||||
|
||||
func TestSafeFormatAndMount(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
|
||||
t.Skipf("not supported on GOOS=%s", runtime.GOOS)
|
||||
}
|
||||
mntDir, err := ioutil.TempDir(os.TempDir(), "mount")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tmp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(mntDir)
|
||||
tests := []struct {
|
||||
description string
|
||||
fstype string
|
||||
mountOptions []string
|
||||
execScripts []ExecArgs
|
||||
mountErrs []error
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "Test a read only mount",
|
||||
fstype: "ext4",
|
||||
mountOptions: []string{"ro"},
|
||||
},
|
||||
{
|
||||
description: "Test a normal mount",
|
||||
fstype: "ext4",
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Test 'fsck' fails with exit status 4",
|
||||
fstype: "ext4",
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 4}},
|
||||
},
|
||||
expectedError: fmt.Errorf("'fsck' found errors on device /dev/foo but could not correct them"),
|
||||
},
|
||||
{
|
||||
description: "Test 'fsck' fails with exit status 1 (errors found and corrected)",
|
||||
fstype: "ext4",
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Test 'fsck' fails with exit status other than 1 and 4 (likely unformatted device)",
|
||||
fstype: "ext4",
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 8}},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Test that 'blkid' is called and fails",
|
||||
fstype: "ext4",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "DEVNAME=/dev/foo\nTYPE=ext4\n", nil},
|
||||
},
|
||||
expectedError: fmt.Errorf("unknown filesystem type '(null)'"),
|
||||
},
|
||||
{
|
||||
description: "Test that 'blkid' is called and confirms unformatted disk, format fails",
|
||||
fstype: "ext4",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 2}},
|
||||
{"mkfs.ext4", []string{"-F", "-m0", "/dev/foo"}, "", fmt.Errorf("formatting failed")},
|
||||
},
|
||||
expectedError: fmt.Errorf("formatting failed"),
|
||||
},
|
||||
{
|
||||
description: "Test that 'blkid' is called and confirms unformatted disk, format passes, second mount fails",
|
||||
fstype: "ext4",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), fmt.Errorf("Still cannot mount")},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 2}},
|
||||
{"mkfs.ext4", []string{"-F", "-m0", "/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: fmt.Errorf("Still cannot mount"),
|
||||
},
|
||||
{
|
||||
description: "Test that 'blkid' is called and confirms unformatted disk, format passes, second mount passes",
|
||||
fstype: "ext4",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 2}},
|
||||
{"mkfs.ext4", []string{"-F", "-m0", "/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "Test that 'blkid' is called and confirms unformatted disk, format passes, second mount passes with ext3",
|
||||
fstype: "ext3",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 2}},
|
||||
{"mkfs.ext3", []string{"-F", "-m0", "/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "test that none ext4 fs does not get called with ext4 options.",
|
||||
fstype: "xfs",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 2}},
|
||||
{"mkfs.xfs", []string{"/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "Test that 'blkid' is called and reports ext4 partition",
|
||||
fstype: "ext3",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "DEVNAME=/dev/foo\nPTTYPE=dos\n", nil},
|
||||
},
|
||||
expectedError: fmt.Errorf("failed to mount the volume as \"ext3\", it already contains unknown data, probably partitions. Mount error: unknown filesystem type '(null)'"),
|
||||
},
|
||||
{
|
||||
description: "Test that 'blkid' is called but has some usage or other errors (an exit code of 4 is returned)",
|
||||
fstype: "xfs",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &testingexec.FakeExitError{Status: 4}},
|
||||
{"mkfs.xfs", []string{"/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: fmt.Errorf("exit 4"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
fakeMounter := ErrorMounter{NewFakeMounter(nil), 0, test.mountErrs}
|
||||
fakeExec := &testingexec.FakeExec{ExactOrder: true}
|
||||
for _, script := range test.execScripts {
|
||||
fakeCmd := &testingexec.FakeCmd{}
|
||||
cmdAction := makeFakeCmd(fakeCmd, script.command, script.args...)
|
||||
outputAction := makeFakeOutput(script.output, script.err)
|
||||
fakeCmd.CombinedOutputScript = append(fakeCmd.CombinedOutputScript, outputAction)
|
||||
fakeExec.CommandScript = append(fakeExec.CommandScript, cmdAction)
|
||||
}
|
||||
mounter := SafeFormatAndMount{
|
||||
Interface: &fakeMounter,
|
||||
Exec: fakeExec,
|
||||
}
|
||||
|
||||
device := "/dev/foo"
|
||||
dest := mntDir
|
||||
err := mounter.FormatAndMount(device, dest, test.fstype, test.mountOptions)
|
||||
if test.expectedError == nil {
|
||||
if err != nil {
|
||||
t.Errorf("test \"%s\" unexpected non-error: %v", test.description, err)
|
||||
}
|
||||
|
||||
// Check that something was mounted on the directory
|
||||
isNotMountPoint, err := fakeMounter.IsLikelyNotMountPoint(dest)
|
||||
if err != nil || isNotMountPoint {
|
||||
t.Errorf("test \"%s\" the directory was not mounted", test.description)
|
||||
}
|
||||
|
||||
//check that the correct device was mounted
|
||||
mountedDevice, _, err := GetDeviceNameFromMount(fakeMounter.FakeMounter, dest)
|
||||
if err != nil || mountedDevice != device {
|
||||
t.Errorf("test \"%s\" the correct device was not mounted", test.description)
|
||||
}
|
||||
} else {
|
||||
if err == nil || !strings.HasPrefix(err.Error(), test.expectedError.Error()) {
|
||||
t.Errorf("test \"%s\" unexpected error: \n [%v]. \nExpecting [%v]", test.description, err, test.expectedError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeFakeCmd(fakeCmd *testingexec.FakeCmd, cmd string, args ...string) testingexec.FakeCommandAction {
|
||||
c := cmd
|
||||
a := args
|
||||
return func(cmd string, args ...string) exec.Cmd {
|
||||
command := testingexec.InitFakeCmd(fakeCmd, c, a...)
|
||||
return command
|
||||
}
|
||||
}
|
||||
|
||||
func makeFakeOutput(output string, err error) testingexec.FakeCombinedOutputAction {
|
||||
o := output
|
||||
return func() ([]byte, error) {
|
||||
return []byte(o), err
|
||||
}
|
||||
}
|
||||
121
vendor/k8s.io/utils/net/ipnet.go
generated
vendored
121
vendor/k8s.io/utils/net/ipnet.go
generated
vendored
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
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 net
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IPNetSet maps string to net.IPNet.
|
||||
type IPNetSet map[string]*net.IPNet
|
||||
|
||||
// ParseIPNets parses string slice to IPNetSet.
|
||||
func ParseIPNets(specs ...string) (IPNetSet, error) {
|
||||
ipnetset := make(IPNetSet)
|
||||
for _, spec := range specs {
|
||||
spec = strings.TrimSpace(spec)
|
||||
_, ipnet, err := net.ParseCIDR(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k := ipnet.String() // In case of normalization
|
||||
ipnetset[k] = ipnet
|
||||
}
|
||||
return ipnetset, nil
|
||||
}
|
||||
|
||||
// Insert adds items to the set.
|
||||
func (s IPNetSet) Insert(items ...*net.IPNet) {
|
||||
for _, item := range items {
|
||||
s[item.String()] = item
|
||||
}
|
||||
}
|
||||
|
||||
// Delete removes all items from the set.
|
||||
func (s IPNetSet) Delete(items ...*net.IPNet) {
|
||||
for _, item := range items {
|
||||
delete(s, item.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Has returns true if and only if item is contained in the set.
|
||||
func (s IPNetSet) Has(item *net.IPNet) bool {
|
||||
_, contained := s[item.String()]
|
||||
return contained
|
||||
}
|
||||
|
||||
// HasAll returns true if and only if all items are contained in the set.
|
||||
func (s IPNetSet) HasAll(items ...*net.IPNet) bool {
|
||||
for _, item := range items {
|
||||
if !s.Has(item) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Difference returns a set of objects that are not in s2
|
||||
// For example:
|
||||
// s1 = {a1, a2, a3}
|
||||
// s2 = {a1, a2, a4, a5}
|
||||
// s1.Difference(s2) = {a3}
|
||||
// s2.Difference(s1) = {a4, a5}
|
||||
func (s IPNetSet) Difference(s2 IPNetSet) IPNetSet {
|
||||
result := make(IPNetSet)
|
||||
for k, i := range s {
|
||||
_, found := s2[k]
|
||||
if found {
|
||||
continue
|
||||
}
|
||||
result[k] = i
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// StringSlice returns a []string with the String representation of each element in the set.
|
||||
// Order is undefined.
|
||||
func (s IPNetSet) StringSlice() []string {
|
||||
a := make([]string, 0, len(s))
|
||||
for k := range s {
|
||||
a = append(a, k)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsSuperset returns true if and only if s1 is a superset of s2.
|
||||
func (s IPNetSet) IsSuperset(s2 IPNetSet) bool {
|
||||
for k := range s2 {
|
||||
_, found := s[k]
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equal returns true if and only if s1 is equal (as a set) to s2.
|
||||
// Two sets are equal if their membership is identical.
|
||||
// (In practice, this means same elements, order doesn't matter)
|
||||
func (s IPNetSet) Equal(s2 IPNetSet) bool {
|
||||
return len(s) == len(s2) && s.IsSuperset(s2)
|
||||
}
|
||||
|
||||
// Len returns the size of the set.
|
||||
func (s IPNetSet) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
155
vendor/k8s.io/utils/net/ipnet_test.go
generated
vendored
155
vendor/k8s.io/utils/net/ipnet_test.go
generated
vendored
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
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 net
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func parseIPNet(s string) *net.IPNet {
|
||||
_, net, err := net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return net
|
||||
}
|
||||
|
||||
func TestIPNets(t *testing.T) {
|
||||
s := IPNetSet{}
|
||||
s2 := IPNetSet{}
|
||||
if len(s) != 0 {
|
||||
t.Errorf("Expected len=0: %d", len(s))
|
||||
}
|
||||
a := parseIPNet("1.0.0.0/8")
|
||||
b := parseIPNet("2.0.0.0/8")
|
||||
c := parseIPNet("3.0.0.0/8")
|
||||
d := parseIPNet("4.0.0.0/8")
|
||||
|
||||
s.Insert(a, b)
|
||||
if len(s) != 2 {
|
||||
t.Errorf("Expected len=2: %d", len(s))
|
||||
}
|
||||
s.Insert(c)
|
||||
if s.Has(d) {
|
||||
t.Errorf("Unexpected contents: %#v", s)
|
||||
}
|
||||
if !s.Has(a) {
|
||||
t.Errorf("Missing contents: %#v", s)
|
||||
}
|
||||
s.Delete(a)
|
||||
if s.Has(a) {
|
||||
t.Errorf("Unexpected contents: %#v", s)
|
||||
}
|
||||
s.Insert(a)
|
||||
if s.HasAll(a, b, d) {
|
||||
t.Errorf("Unexpected contents: %#v", s)
|
||||
}
|
||||
if !s.HasAll(a, b) {
|
||||
t.Errorf("Missing contents: %#v", s)
|
||||
}
|
||||
s2.Insert(a, b, d)
|
||||
if s.IsSuperset(s2) {
|
||||
t.Errorf("Unexpected contents: %#v", s)
|
||||
}
|
||||
s2.Delete(d)
|
||||
if !s.IsSuperset(s2) {
|
||||
t.Errorf("Missing contents: %#v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPNetSetDeleteMultiples(t *testing.T) {
|
||||
s := IPNetSet{}
|
||||
a := parseIPNet("1.0.0.0/8")
|
||||
b := parseIPNet("2.0.0.0/8")
|
||||
c := parseIPNet("3.0.0.0/8")
|
||||
|
||||
s.Insert(a, b, c)
|
||||
if len(s) != 3 {
|
||||
t.Errorf("Expected len=3: %d", len(s))
|
||||
}
|
||||
|
||||
s.Delete(a, c)
|
||||
if len(s) != 1 {
|
||||
t.Errorf("Expected len=1: %d", len(s))
|
||||
}
|
||||
if s.Has(a) {
|
||||
t.Errorf("Unexpected contents: %#v", s)
|
||||
}
|
||||
if s.Has(c) {
|
||||
t.Errorf("Unexpected contents: %#v", s)
|
||||
}
|
||||
if !s.Has(b) {
|
||||
t.Errorf("Missing contents: %#v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewIPSet(t *testing.T) {
|
||||
s, err := ParseIPNets("1.0.0.0/8", "2.0.0.0/8", "3.0.0.0/8")
|
||||
if err != nil {
|
||||
t.Errorf("error parsing IPNets: %v", err)
|
||||
}
|
||||
if len(s) != 3 {
|
||||
t.Errorf("Expected len=3: %d", len(s))
|
||||
}
|
||||
a := parseIPNet("1.0.0.0/8")
|
||||
b := parseIPNet("2.0.0.0/8")
|
||||
c := parseIPNet("3.0.0.0/8")
|
||||
|
||||
if !s.Has(a) || !s.Has(b) || !s.Has(c) {
|
||||
t.Errorf("Unexpected contents: %#v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPNetSetDifference(t *testing.T) {
|
||||
l, err := ParseIPNets("1.0.0.0/8", "2.0.0.0/8", "3.0.0.0/8")
|
||||
if err != nil {
|
||||
t.Errorf("error parsing IPNets: %v", err)
|
||||
}
|
||||
r, err := ParseIPNets("1.0.0.0/8", "2.0.0.0/8", "4.0.0.0/8", "5.0.0.0/8")
|
||||
if err != nil {
|
||||
t.Errorf("error parsing IPNets: %v", err)
|
||||
}
|
||||
c := l.Difference(r)
|
||||
d := r.Difference(l)
|
||||
if len(c) != 1 {
|
||||
t.Errorf("Expected len=1: %d", len(c))
|
||||
}
|
||||
if !c.Has(parseIPNet("3.0.0.0/8")) {
|
||||
t.Errorf("Unexpected contents: %#v", c)
|
||||
}
|
||||
if len(d) != 2 {
|
||||
t.Errorf("Expected len=2: %d", len(d))
|
||||
}
|
||||
if !d.Has(parseIPNet("4.0.0.0/8")) || !d.Has(parseIPNet("5.0.0.0/8")) {
|
||||
t.Errorf("Unexpected contents: %#v", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPNetSetList(t *testing.T) {
|
||||
s, err := ParseIPNets("3.0.0.0/8", "1.0.0.0/8", "2.0.0.0/8")
|
||||
if err != nil {
|
||||
t.Errorf("error parsing IPNets: %v", err)
|
||||
}
|
||||
l := s.StringSlice()
|
||||
sort.Strings(l)
|
||||
if !reflect.DeepEqual(l, []string{"1.0.0.0/8", "2.0.0.0/8", "3.0.0.0/8"}) {
|
||||
t.Errorf("List gave unexpected result: %#v", l)
|
||||
}
|
||||
}
|
||||
189
vendor/k8s.io/utils/net/net.go
generated
vendored
189
vendor/k8s.io/utils/net/net.go
generated
vendored
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
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 net
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ParseCIDRs parses a list of cidrs and return error if any is invalid.
|
||||
// order is maintained
|
||||
func ParseCIDRs(cidrsString []string) ([]*net.IPNet, error) {
|
||||
cidrs := make([]*net.IPNet, 0, len(cidrsString))
|
||||
for _, cidrString := range cidrsString {
|
||||
_, cidr, err := net.ParseCIDR(cidrString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse cidr value:%q with error:%v", cidrString, err)
|
||||
}
|
||||
cidrs = append(cidrs, cidr)
|
||||
}
|
||||
return cidrs, nil
|
||||
}
|
||||
|
||||
// IsDualStackIPs returns if a slice of ips is:
|
||||
// - all are valid ips
|
||||
// - at least one ip from each family (v4 or v6)
|
||||
func IsDualStackIPs(ips []net.IP) (bool, error) {
|
||||
v4Found := false
|
||||
v6Found := false
|
||||
for _, ip := range ips {
|
||||
if ip == nil {
|
||||
return false, fmt.Errorf("ip %v is invalid", ip)
|
||||
}
|
||||
|
||||
if v4Found && v6Found {
|
||||
continue
|
||||
}
|
||||
|
||||
if IsIPv6(ip) {
|
||||
v6Found = true
|
||||
continue
|
||||
}
|
||||
|
||||
v4Found = true
|
||||
}
|
||||
|
||||
return (v4Found && v6Found), nil
|
||||
}
|
||||
|
||||
// IsDualStackIPStrings returns if
|
||||
// - all are valid ips
|
||||
// - at least one ip from each family (v4 or v6)
|
||||
func IsDualStackIPStrings(ips []string) (bool, error) {
|
||||
parsedIPs := make([]net.IP, 0, len(ips))
|
||||
for _, ip := range ips {
|
||||
parsedIP := net.ParseIP(ip)
|
||||
parsedIPs = append(parsedIPs, parsedIP)
|
||||
}
|
||||
return IsDualStackIPs(parsedIPs)
|
||||
}
|
||||
|
||||
// IsDualStackCIDRs returns if
|
||||
// - all are valid cidrs
|
||||
// - at least one cidr from each family (v4 or v6)
|
||||
func IsDualStackCIDRs(cidrs []*net.IPNet) (bool, error) {
|
||||
v4Found := false
|
||||
v6Found := false
|
||||
for _, cidr := range cidrs {
|
||||
if cidr == nil {
|
||||
return false, fmt.Errorf("cidr %v is invalid", cidr)
|
||||
}
|
||||
|
||||
if v4Found && v6Found {
|
||||
continue
|
||||
}
|
||||
|
||||
if IsIPv6(cidr.IP) {
|
||||
v6Found = true
|
||||
continue
|
||||
}
|
||||
v4Found = true
|
||||
}
|
||||
|
||||
return v4Found && v6Found, nil
|
||||
}
|
||||
|
||||
// IsDualStackCIDRStrings returns if
|
||||
// - all are valid cidrs
|
||||
// - at least one cidr from each family (v4 or v6)
|
||||
func IsDualStackCIDRStrings(cidrs []string) (bool, error) {
|
||||
parsedCIDRs, err := ParseCIDRs(cidrs)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return IsDualStackCIDRs(parsedCIDRs)
|
||||
}
|
||||
|
||||
// IsIPv6 returns if netIP is IPv6.
|
||||
func IsIPv6(netIP net.IP) bool {
|
||||
return netIP != nil && netIP.To4() == nil
|
||||
}
|
||||
|
||||
// IsIPv6String returns if ip is IPv6.
|
||||
func IsIPv6String(ip string) bool {
|
||||
netIP := net.ParseIP(ip)
|
||||
return IsIPv6(netIP)
|
||||
}
|
||||
|
||||
// IsIPv6CIDRString returns if cidr is IPv6.
|
||||
// This assumes cidr is a valid CIDR.
|
||||
func IsIPv6CIDRString(cidr string) bool {
|
||||
ip, _, _ := net.ParseCIDR(cidr)
|
||||
return IsIPv6(ip)
|
||||
}
|
||||
|
||||
// IsIPv6CIDR returns if a cidr is ipv6
|
||||
func IsIPv6CIDR(cidr *net.IPNet) bool {
|
||||
ip := cidr.IP
|
||||
return IsIPv6(ip)
|
||||
}
|
||||
|
||||
// ParsePort parses a string representing an IP port. If the string is not a
|
||||
// valid port number, this returns an error.
|
||||
func ParsePort(port string, allowZero bool) (int, error) {
|
||||
portInt, err := strconv.ParseUint(port, 10, 16)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if portInt == 0 && !allowZero {
|
||||
return 0, errors.New("0 is not a valid port number")
|
||||
}
|
||||
return int(portInt), nil
|
||||
}
|
||||
|
||||
// BigForIP creates a big.Int based on the provided net.IP
|
||||
func BigForIP(ip net.IP) *big.Int {
|
||||
b := ip.To4()
|
||||
if b == nil {
|
||||
b = ip.To16()
|
||||
}
|
||||
return big.NewInt(0).SetBytes(b)
|
||||
}
|
||||
|
||||
// AddIPOffset adds the provided integer offset to a base big.Int representing a
|
||||
// net.IP
|
||||
func AddIPOffset(base *big.Int, offset int) net.IP {
|
||||
return net.IP(big.NewInt(0).Add(base, big.NewInt(int64(offset))).Bytes())
|
||||
}
|
||||
|
||||
// RangeSize returns the size of a range in valid addresses.
|
||||
// returns the size of the subnet (or math.MaxInt64 if the range size would overflow int64)
|
||||
func RangeSize(subnet *net.IPNet) int64 {
|
||||
ones, bits := subnet.Mask.Size()
|
||||
if bits == 32 && (bits-ones) >= 31 || bits == 128 && (bits-ones) >= 127 {
|
||||
return 0
|
||||
}
|
||||
// this checks that we are not overflowing an int64
|
||||
if bits-ones >= 63 {
|
||||
return math.MaxInt64
|
||||
}
|
||||
return int64(1) << uint(bits-ones)
|
||||
}
|
||||
|
||||
// GetIndexedIP returns a net.IP that is subnet.IP + index in the contiguous IP space.
|
||||
func GetIndexedIP(subnet *net.IPNet, index int) (net.IP, error) {
|
||||
ip := AddIPOffset(BigForIP(subnet.IP), index)
|
||||
if !subnet.Contains(ip) {
|
||||
return nil, fmt.Errorf("can't generate IP with index %d from subnet. subnet too small. subnet: %q", index, subnet)
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
607
vendor/k8s.io/utils/net/net_test.go
generated
vendored
607
vendor/k8s.io/utils/net/net_test.go
generated
vendored
@@ -1,607 +0,0 @@
|
||||
/*
|
||||
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 net
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseCIDRs(t *testing.T) {
|
||||
testCases := []struct {
|
||||
cidrs []string
|
||||
errString string
|
||||
errorExpected bool
|
||||
}{
|
||||
{
|
||||
cidrs: []string{},
|
||||
errString: "should not return an error for an empty slice",
|
||||
errorExpected: false,
|
||||
},
|
||||
{
|
||||
cidrs: []string{"10.0.0.0/8", "not-a-valid-cidr", "2000::/10"},
|
||||
errString: "should return error for bad cidr",
|
||||
errorExpected: true,
|
||||
},
|
||||
{
|
||||
cidrs: []string{"10.0.0.0/8", "2000::/10"},
|
||||
errString: "should not return error for good cidrs",
|
||||
errorExpected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
cidrs, err := ParseCIDRs(tc.cidrs)
|
||||
if tc.errorExpected {
|
||||
if err == nil {
|
||||
t.Errorf("%v", tc.errString)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("%v error:%v", tc.errString, err)
|
||||
}
|
||||
|
||||
// validate lengths
|
||||
if len(cidrs) != len(tc.cidrs) {
|
||||
t.Errorf("cidrs should be of the same lengths %v != %v", len(cidrs), len(tc.cidrs))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
func TestDualStackIPs(t *testing.T) {
|
||||
testCases := []struct {
|
||||
ips []string
|
||||
errMessage string
|
||||
expectedResult bool
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
ips: []string{"1.1.1.1"},
|
||||
errMessage: "should fail because length is not at least 2",
|
||||
expectedResult: false,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
ips: []string{},
|
||||
errMessage: "should fail because length is not at least 2",
|
||||
expectedResult: false,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
ips: []string{"1.1.1.1", "2.2.2.2", "3.3.3.3"},
|
||||
errMessage: "should fail because all are v4",
|
||||
expectedResult: false,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
ips: []string{"fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff", "fd92:20ba:ca:34f7:ffff:ffff:ffff:fff0", "fd92:20ba:ca:34f7:ffff:ffff:ffff:fff1"},
|
||||
errMessage: "should fail because all are v6",
|
||||
expectedResult: false,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
ips: []string{"1.1.1.1", "not-a-valid-ip"},
|
||||
errMessage: "should fail because 2nd ip is invalid",
|
||||
expectedResult: false,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
ips: []string{"not-a-valid-ip", "fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff"},
|
||||
errMessage: "should fail because 1st ip is invalid",
|
||||
expectedResult: false,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
ips: []string{"1.1.1.1", "fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff"},
|
||||
errMessage: "expected success, but found failure",
|
||||
expectedResult: true,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
ips: []string{"fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff", "1.1.1.1", "fd92:20ba:ca:34f7:ffff:ffff:ffff:fff0"},
|
||||
errMessage: "expected success, but found failure",
|
||||
expectedResult: true,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
ips: []string{"1.1.1.1", "fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff", "10.0.0.0"},
|
||||
errMessage: "expected success, but found failure",
|
||||
expectedResult: true,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
ips: []string{"fd92:20ba:ca:34f7:ffff:ffff:ffff:ffff", "1.1.1.1"},
|
||||
errMessage: "expected success, but found failure",
|
||||
expectedResult: true,
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
// for each test case, test the regular func and the string func
|
||||
for _, tc := range testCases {
|
||||
dualStack, err := IsDualStackIPStrings(tc.ips)
|
||||
if err == nil && tc.expectError {
|
||||
t.Errorf("%s", tc.errMessage)
|
||||
continue
|
||||
}
|
||||
if err != nil && !tc.expectError {
|
||||
t.Errorf("failed to run test case for %v, error: %v", tc.ips, err)
|
||||
continue
|
||||
}
|
||||
if dualStack != tc.expectedResult {
|
||||
t.Errorf("%v for %v", tc.errMessage, tc.ips)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
ips := make([]net.IP, 0, len(tc.ips))
|
||||
for _, ip := range tc.ips {
|
||||
parsedIP := net.ParseIP(ip)
|
||||
ips = append(ips, parsedIP)
|
||||
}
|
||||
dualStack, err := IsDualStackIPs(ips)
|
||||
if err == nil && tc.expectError {
|
||||
t.Errorf("%s", tc.errMessage)
|
||||
continue
|
||||
}
|
||||
if err != nil && !tc.expectError {
|
||||
t.Errorf("failed to run test case for %v, error: %v", tc.ips, err)
|
||||
continue
|
||||
}
|
||||
if dualStack != tc.expectedResult {
|
||||
t.Errorf("%v for %v", tc.errMessage, tc.ips)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDualStackCIDRs(t *testing.T) {
|
||||
testCases := []struct {
|
||||
cidrs []string
|
||||
errMessage string
|
||||
expectedResult bool
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
cidrs: []string{"10.10.10.10/8"},
|
||||
errMessage: "should fail because length is not at least 2",
|
||||
expectedResult: false,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
cidrs: []string{},
|
||||
errMessage: "should fail because length is not at least 2",
|
||||
expectedResult: false,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
cidrs: []string{"10.10.10.10/8", "20.20.20.20/8", "30.30.30.30/8"},
|
||||
errMessage: "should fail because all cidrs are v4",
|
||||
expectedResult: false,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
cidrs: []string{"2000::/10", "3000::/10"},
|
||||
errMessage: "should fail because all cidrs are v6",
|
||||
expectedResult: false,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
cidrs: []string{"10.10.10.10/8", "not-a-valid-cidr"},
|
||||
errMessage: "should fail because 2nd cidr is invalid",
|
||||
expectedResult: false,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
cidrs: []string{"not-a-valid-ip", "2000::/10"},
|
||||
errMessage: "should fail because 1st cidr is invalid",
|
||||
expectedResult: false,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
cidrs: []string{"10.10.10.10/8", "2000::/10"},
|
||||
errMessage: "expected success, but found failure",
|
||||
expectedResult: true,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
cidrs: []string{"2000::/10", "10.10.10.10/8"},
|
||||
errMessage: "expected success, but found failure",
|
||||
expectedResult: true,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
cidrs: []string{"2000::/10", "10.10.10.10/8", "3000::/10"},
|
||||
errMessage: "expected success, but found failure",
|
||||
expectedResult: true,
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
// for each test case, test the regular func and the string func
|
||||
for _, tc := range testCases {
|
||||
dualStack, err := IsDualStackCIDRStrings(tc.cidrs)
|
||||
if err == nil && tc.expectError {
|
||||
t.Errorf("%s", tc.errMessage)
|
||||
continue
|
||||
}
|
||||
if err != nil && !tc.expectError {
|
||||
t.Errorf("failed to run test case for %v, error: %v", tc.cidrs, err)
|
||||
continue
|
||||
}
|
||||
if dualStack != tc.expectedResult {
|
||||
t.Errorf("%v for %v", tc.errMessage, tc.cidrs)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
cidrs := make([]*net.IPNet, 0, len(tc.cidrs))
|
||||
for _, cidr := range tc.cidrs {
|
||||
_, parsedCIDR, _ := net.ParseCIDR(cidr)
|
||||
cidrs = append(cidrs, parsedCIDR)
|
||||
}
|
||||
|
||||
dualStack, err := IsDualStackCIDRs(cidrs)
|
||||
if err == nil && tc.expectError {
|
||||
t.Errorf("%s", tc.errMessage)
|
||||
continue
|
||||
}
|
||||
if err != nil && !tc.expectError {
|
||||
t.Errorf("failed to run test case for %v, error: %v", tc.cidrs, err)
|
||||
continue
|
||||
}
|
||||
if dualStack != tc.expectedResult {
|
||||
t.Errorf("%v for %v", tc.errMessage, tc.cidrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIPv6String(t *testing.T) {
|
||||
testCases := []struct {
|
||||
ip string
|
||||
expectIPv6 bool
|
||||
}{
|
||||
{
|
||||
ip: "127.0.0.1",
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: "192.168.0.0",
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: "1.2.3.4",
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: "bad ip",
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: "::1",
|
||||
expectIPv6: true,
|
||||
},
|
||||
{
|
||||
ip: "fd00::600d:f00d",
|
||||
expectIPv6: true,
|
||||
},
|
||||
{
|
||||
ip: "2001:db8::5",
|
||||
expectIPv6: true,
|
||||
},
|
||||
}
|
||||
for i := range testCases {
|
||||
isIPv6 := IsIPv6String(testCases[i].ip)
|
||||
if isIPv6 != testCases[i].expectIPv6 {
|
||||
t.Errorf("[%d] Expect ipv6 %v, got %v", i+1, testCases[i].expectIPv6, isIPv6)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIPv6(t *testing.T) {
|
||||
testCases := []struct {
|
||||
ip net.IP
|
||||
expectIPv6 bool
|
||||
}{
|
||||
{
|
||||
ip: net.IPv4zero,
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: net.IPv4bcast,
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: net.ParseIP("127.0.0.1"),
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: net.ParseIP("10.20.40.40"),
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: net.ParseIP("172.17.3.0"),
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: nil,
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: net.IPv6loopback,
|
||||
expectIPv6: true,
|
||||
},
|
||||
{
|
||||
ip: net.IPv6zero,
|
||||
expectIPv6: true,
|
||||
},
|
||||
{
|
||||
ip: net.ParseIP("fd00::600d:f00d"),
|
||||
expectIPv6: true,
|
||||
},
|
||||
{
|
||||
ip: net.ParseIP("2001:db8::5"),
|
||||
expectIPv6: true,
|
||||
},
|
||||
}
|
||||
for i := range testCases {
|
||||
isIPv6 := IsIPv6(testCases[i].ip)
|
||||
if isIPv6 != testCases[i].expectIPv6 {
|
||||
t.Errorf("[%d] Expect ipv6 %v, got %v", i+1, testCases[i].expectIPv6, isIPv6)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIPv6CIDRString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
cidr string
|
||||
expectResult bool
|
||||
}{
|
||||
{
|
||||
desc: "ipv4 CIDR 1",
|
||||
cidr: "10.0.0.0/8",
|
||||
expectResult: false,
|
||||
},
|
||||
{
|
||||
desc: "ipv4 CIDR 2",
|
||||
cidr: "192.168.0.0/16",
|
||||
expectResult: false,
|
||||
},
|
||||
{
|
||||
desc: "ipv6 CIDR 1",
|
||||
cidr: "::/1",
|
||||
expectResult: true,
|
||||
},
|
||||
{
|
||||
desc: "ipv6 CIDR 2",
|
||||
cidr: "2000::/10",
|
||||
expectResult: true,
|
||||
},
|
||||
{
|
||||
desc: "ipv6 CIDR 3",
|
||||
cidr: "2001:db8::/32",
|
||||
expectResult: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
res := IsIPv6CIDRString(tc.cidr)
|
||||
if res != tc.expectResult {
|
||||
t.Errorf("%v: want IsIPv6CIDRString=%v, got %v", tc.desc, tc.expectResult, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIPv6CIDR(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
cidr string
|
||||
expectResult bool
|
||||
}{
|
||||
{
|
||||
desc: "ipv4 CIDR 1",
|
||||
cidr: "10.0.0.0/8",
|
||||
expectResult: false,
|
||||
},
|
||||
{
|
||||
desc: "ipv4 CIDR 2",
|
||||
cidr: "192.168.0.0/16",
|
||||
expectResult: false,
|
||||
},
|
||||
{
|
||||
desc: "ipv6 CIDR 1",
|
||||
cidr: "::/1",
|
||||
expectResult: true,
|
||||
},
|
||||
{
|
||||
desc: "ipv6 CIDR 2",
|
||||
cidr: "2000::/10",
|
||||
expectResult: true,
|
||||
},
|
||||
{
|
||||
desc: "ipv6 CIDR 3",
|
||||
cidr: "2001:db8::/32",
|
||||
expectResult: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
_, cidr, _ := net.ParseCIDR(tc.cidr)
|
||||
res := IsIPv6CIDR(cidr)
|
||||
if res != tc.expectResult {
|
||||
t.Errorf("%v: want IsIPv6CIDR=%v, got %v", tc.desc, tc.expectResult, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePort(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
port string
|
||||
allowZero bool
|
||||
expectedPort int
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "valid port: 1",
|
||||
port: "1",
|
||||
expectedPort: 1,
|
||||
},
|
||||
{
|
||||
name: "valid port: 1234",
|
||||
port: "1234",
|
||||
expectedPort: 1234,
|
||||
},
|
||||
{
|
||||
name: "valid port: 65535",
|
||||
port: "65535",
|
||||
expectedPort: 65535,
|
||||
},
|
||||
{
|
||||
name: "invalid port: not a number",
|
||||
port: "a",
|
||||
expectedError: true,
|
||||
allowZero: false,
|
||||
},
|
||||
{
|
||||
name: "invalid port: too small",
|
||||
port: "0",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid port: negative",
|
||||
port: "-10",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid port: too big",
|
||||
port: "65536",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "zero port: allowed",
|
||||
port: "0",
|
||||
allowZero: true,
|
||||
},
|
||||
{
|
||||
name: "zero port: not allowed",
|
||||
port: "0",
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t *testing.T) {
|
||||
actualPort, actualError := ParsePort(rt.port, rt.allowZero)
|
||||
|
||||
if actualError != nil && !rt.expectedError {
|
||||
t.Errorf("%s unexpected failure: %v", rt.name, actualError)
|
||||
return
|
||||
}
|
||||
if actualError == nil && rt.expectedError {
|
||||
t.Errorf("%s passed when expected to fail", rt.name)
|
||||
return
|
||||
}
|
||||
if actualPort != rt.expectedPort {
|
||||
t.Errorf("%s returned wrong port: got %d, expected %d", rt.name, actualPort, rt.expectedPort)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRangeSize(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
cidr string
|
||||
addrs int64
|
||||
}{
|
||||
{
|
||||
name: "supported IPv4 cidr",
|
||||
cidr: "192.168.1.0/24",
|
||||
addrs: 256,
|
||||
},
|
||||
{
|
||||
name: "unsupported IPv4 cidr",
|
||||
cidr: "192.168.1.0/1",
|
||||
addrs: 0,
|
||||
},
|
||||
{
|
||||
name: "unsupported IPv6 mask",
|
||||
cidr: "2001:db8::/1",
|
||||
addrs: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
_, cidr, err := net.ParseCIDR(tc.cidr)
|
||||
if err != nil {
|
||||
t.Errorf("failed to parse cidr for test %s, unexpected error: '%s'", tc.name, err)
|
||||
}
|
||||
if size := RangeSize(cidr); size != tc.addrs {
|
||||
t.Errorf("test %s failed. %s should have a range size of %d, got %d",
|
||||
tc.name, tc.cidr, tc.addrs, size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIndexedIP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
cidr string
|
||||
index int
|
||||
expectError bool
|
||||
expectedIP string
|
||||
}{
|
||||
{
|
||||
cidr: "192.168.1.0/24",
|
||||
index: 20,
|
||||
expectError: false,
|
||||
expectedIP: "192.168.1.20",
|
||||
},
|
||||
{
|
||||
cidr: "192.168.1.0/30",
|
||||
index: 10,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
cidr: "192.168.1.0/24",
|
||||
index: 255,
|
||||
expectError: false,
|
||||
expectedIP: "192.168.1.255",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
_, subnet, err := net.ParseCIDR(tc.cidr)
|
||||
if err != nil {
|
||||
t.Errorf("failed to parse cidr %s, unexpected error: '%s'", tc.cidr, err)
|
||||
}
|
||||
|
||||
ip, err := GetIndexedIP(subnet, tc.index)
|
||||
if err == nil && tc.expectError || err != nil && !tc.expectError {
|
||||
t.Errorf("expectedError is %v and err is %s", tc.expectError, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
ipString := ip.String()
|
||||
if ipString != tc.expectedIP {
|
||||
t.Errorf("expected %s but instead got %s", tc.expectedIP, ipString)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
10
vendor/k8s.io/utils/nsenter/OWNERS
generated
vendored
10
vendor/k8s.io/utils/nsenter/OWNERS
generated
vendored
@@ -1,10 +0,0 @@
|
||||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
reviewers:
|
||||
- jsafrane
|
||||
- msau42
|
||||
- cofyc
|
||||
approvers:
|
||||
- jsafrane
|
||||
- msau42
|
||||
- cofyc
|
||||
258
vendor/k8s.io/utils/nsenter/nsenter.go
generated
vendored
258
vendor/k8s.io/utils/nsenter/nsenter.go
generated
vendored
@@ -1,258 +0,0 @@
|
||||
// +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"
|
||||
"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{}
|
||||
311
vendor/k8s.io/utils/nsenter/nsenter_test.go
generated
vendored
311
vendor/k8s.io/utils/nsenter/nsenter_test.go
generated
vendored
@@ -1,311 +0,0 @@
|
||||
// +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 nsenter
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
func TestExec(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
command string
|
||||
args []string
|
||||
expectedOutput string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "simple command",
|
||||
command: "echo",
|
||||
args: []string{"hello", "world"},
|
||||
expectedOutput: "hello world\n",
|
||||
},
|
||||
{
|
||||
name: "nozero exit code",
|
||||
command: "false",
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
executor := fakeExec{
|
||||
rootfsPath: "/rootfs",
|
||||
}
|
||||
for _, test := range tests {
|
||||
ns := NSEnter{
|
||||
hostRootFsPath: "/rootfs",
|
||||
executor: executor,
|
||||
}
|
||||
cmd := ns.Exec(test.command, test.args)
|
||||
outBytes, err := cmd.CombinedOutput()
|
||||
out := string(outBytes)
|
||||
if err != nil && !test.expectError {
|
||||
t.Errorf("Test %q: unexpected error: %s", test.name, err)
|
||||
}
|
||||
if err == nil && test.expectError {
|
||||
t.Errorf("Test %q: expected error, got none", test.name)
|
||||
}
|
||||
if test.expectedOutput != out {
|
||||
t.Errorf("test %q: expected output %q, got %q", test.name, test.expectedOutput, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestKubeletPath(t *testing.T) {
|
||||
tests := []struct {
|
||||
rootfs string
|
||||
hostpath string
|
||||
expectedKubeletPath string
|
||||
}{
|
||||
{
|
||||
// simple join
|
||||
"/rootfs",
|
||||
"/some/path",
|
||||
"/rootfs/some/path",
|
||||
},
|
||||
{
|
||||
// squash slashes
|
||||
"/rootfs/",
|
||||
"//some/path",
|
||||
"/rootfs/some/path",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
ns := NSEnter{
|
||||
hostRootFsPath: test.rootfs,
|
||||
}
|
||||
out := ns.KubeletPath(test.hostpath)
|
||||
if out != test.expectedKubeletPath {
|
||||
t.Errorf("Expected path %q, got %q", test.expectedKubeletPath, out)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalSymlinks(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
mustExist bool
|
||||
prepare func(tmpdir string) (src string, expectedDst string, err error)
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "simple file /src",
|
||||
mustExist: true,
|
||||
prepare: func(tmpdir string) (src string, expectedDst string, err error) {
|
||||
src = filepath.Join(tmpdir, "src")
|
||||
err = ioutil.WriteFile(src, []byte{}, 0644)
|
||||
return src, src, err
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "non-existing file /src",
|
||||
mustExist: true,
|
||||
prepare: func(tmpdir string) (src string, expectedDst string, err error) {
|
||||
src = filepath.Join(tmpdir, "src")
|
||||
return src, "", nil
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "non-existing file /src/ with mustExist=false",
|
||||
mustExist: false,
|
||||
prepare: func(tmpdir string) (src string, expectedDst string, err error) {
|
||||
src = filepath.Join(tmpdir, "src")
|
||||
return src, src, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "non-existing file /existing/path/src with mustExist=false with existing directories",
|
||||
mustExist: false,
|
||||
prepare: func(tmpdir string) (src string, expectedDst string, err error) {
|
||||
src = filepath.Join(tmpdir, "existing/path")
|
||||
if err := os.MkdirAll(src, 0755); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
src = filepath.Join(src, "src")
|
||||
return src, src, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "simple symlink /src -> /dst",
|
||||
mustExist: false,
|
||||
prepare: func(tmpdir string) (src string, expectedDst string, err error) {
|
||||
dst := filepath.Join(tmpdir, "dst")
|
||||
if err = ioutil.WriteFile(dst, []byte{}, 0644); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
src = filepath.Join(tmpdir, "src")
|
||||
err = os.Symlink(dst, src)
|
||||
return src, dst, err
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dangling symlink /src -> /non-existing-path",
|
||||
mustExist: true,
|
||||
prepare: func(tmpdir string) (src string, expectedDst string, err error) {
|
||||
dst := filepath.Join(tmpdir, "non-existing-path")
|
||||
src = filepath.Join(tmpdir, "src")
|
||||
err = os.Symlink(dst, src)
|
||||
return src, "", err
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "dangling symlink /src -> /non-existing-path with mustExist=false",
|
||||
mustExist: false,
|
||||
prepare: func(tmpdir string) (src string, expectedDst string, err error) {
|
||||
dst := filepath.Join(tmpdir, "non-existing-path")
|
||||
src = filepath.Join(tmpdir, "src")
|
||||
err = os.Symlink(dst, src)
|
||||
return src, dst, err
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "symlink to directory /src/file, where /src is link to /dst",
|
||||
mustExist: true,
|
||||
prepare: func(tmpdir string) (src string, expectedDst string, err error) {
|
||||
dst := filepath.Join(tmpdir, "dst")
|
||||
if err = os.Mkdir(dst, 0755); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
dstFile := filepath.Join(dst, "file")
|
||||
if err = ioutil.WriteFile(dstFile, []byte{}, 0644); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
src = filepath.Join(tmpdir, "src")
|
||||
if err = os.Symlink(dst, src); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
srcFile := filepath.Join(src, "file")
|
||||
return srcFile, dstFile, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "symlink to non-existing directory: /src/file, where /src is link to /dst and dst does not exist",
|
||||
mustExist: true,
|
||||
prepare: func(tmpdir string) (src string, expectedDst string, err error) {
|
||||
dst := filepath.Join(tmpdir, "dst")
|
||||
|
||||
src = filepath.Join(tmpdir, "src")
|
||||
if err = os.Symlink(dst, src); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
srcFile := filepath.Join(src, "file")
|
||||
return srcFile, "", nil
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "symlink to non-existing directory: /src/file, where /src is link to /dst and dst does not exist with mustExist=false",
|
||||
mustExist: false,
|
||||
prepare: func(tmpdir string) (src string, expectedDst string, err error) {
|
||||
dst := filepath.Join(tmpdir, "dst")
|
||||
dstFile := filepath.Join(dst, "file")
|
||||
|
||||
src = filepath.Join(tmpdir, "src")
|
||||
if err = os.Symlink(dst, src); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
srcFile := filepath.Join(src, "file")
|
||||
return srcFile, dstFile, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
ns := NSEnter{
|
||||
hostRootFsPath: "/rootfs",
|
||||
executor: fakeExec{
|
||||
rootfsPath: "/rootfs",
|
||||
},
|
||||
}
|
||||
|
||||
tmpdir, err := ioutil.TempDir("", "nsenter-hostpath-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
src, expectedDst, err := test.prepare(tmpdir)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
dst, err := ns.EvalSymlinks(src, test.mustExist)
|
||||
if err != nil && !test.expectError {
|
||||
t.Errorf("Test %q: unexpected error: %s", test.name, err)
|
||||
}
|
||||
if err == nil && test.expectError {
|
||||
t.Errorf("Test %q: expected error, got none", test.name)
|
||||
}
|
||||
if dst != expectedDst {
|
||||
t.Errorf("Test %q: expected destination %q, got %q", test.name, expectedDst, dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewNsenter(t *testing.T) {
|
||||
// Create a symlink /tmp/xyz/rootfs -> / and use it as rootfs path
|
||||
// It should resolve all binaries correctly, the test runs on Linux
|
||||
|
||||
tmpdir, err := ioutil.TempDir("", "nsenter-hostpath-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
rootfs := filepath.Join(tmpdir, "rootfs")
|
||||
if err = os.Symlink("/", rootfs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = NewNsenter(rootfs, exec.New())
|
||||
if err != nil {
|
||||
t.Errorf("Error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewNsenterError(t *testing.T) {
|
||||
// Create empty dir /tmp/xyz/rootfs and use it as rootfs path
|
||||
// It should resolve all binaries correctly, the test runs on Linux
|
||||
|
||||
tmpdir, err := ioutil.TempDir("", "nsenter-hostpath-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
rootfs := filepath.Join(tmpdir, "rootfs")
|
||||
if err = os.MkdirAll(rootfs, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = NewNsenter(rootfs, exec.New())
|
||||
if err == nil {
|
||||
t.Errorf("Expected error, got none")
|
||||
}
|
||||
}
|
||||
79
vendor/k8s.io/utils/nsenter/nsenter_unsupported.go
generated
vendored
79
vendor/k8s.io/utils/nsenter/nsenter_unsupported.go
generated
vendored
@@ -1,79 +0,0 @@
|
||||
// +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{}
|
||||
78
vendor/k8s.io/utils/path/file.go
generated
vendored
78
vendor/k8s.io/utils/path/file.go
generated
vendored
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
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 path
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
// LinkTreatment is the base type for constants used by Exists that indicate
|
||||
// how symlinks are treated for existence checks.
|
||||
type LinkTreatment int
|
||||
|
||||
const (
|
||||
// CheckFollowSymlink follows the symlink and verifies that the target of
|
||||
// the symlink exists.
|
||||
CheckFollowSymlink LinkTreatment = iota
|
||||
|
||||
// CheckSymlinkOnly does not follow the symlink and verfies only that they
|
||||
// symlink itself exists.
|
||||
CheckSymlinkOnly
|
||||
)
|
||||
|
||||
// ErrInvalidLinkTreatment indicates that the link treatment behavior requested
|
||||
// is not a valid behavior.
|
||||
var ErrInvalidLinkTreatment = errors.New("unknown link behavior")
|
||||
|
||||
// Exists checks if specified file, directory, or symlink exists. The behavior
|
||||
// of the test depends on the linkBehaviour argument. See LinkTreatment for
|
||||
// more details.
|
||||
func Exists(linkBehavior LinkTreatment, filename string) (bool, error) {
|
||||
var err error
|
||||
|
||||
if linkBehavior == CheckFollowSymlink {
|
||||
_, err = os.Stat(filename)
|
||||
} else if linkBehavior == CheckSymlinkOnly {
|
||||
_, err = os.Lstat(filename)
|
||||
} else {
|
||||
return false, ErrInvalidLinkTreatment
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// ReadDirNoStat returns a string of files/directories contained
|
||||
// in dirname without calling lstat on them.
|
||||
func ReadDirNoStat(dirname string) ([]string, error) {
|
||||
if dirname == "" {
|
||||
dirname = "."
|
||||
}
|
||||
|
||||
f, err := os.Open(dirname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return f.Readdirnames(-1)
|
||||
}
|
||||
147
vendor/k8s.io/utils/path/file_test.go
generated
vendored
147
vendor/k8s.io/utils/path/file_test.go
generated
vendored
@@ -1,147 +0,0 @@
|
||||
/*
|
||||
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 path
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFileUtils(t *testing.T) {
|
||||
fs := &afero.Afero{Fs: afero.NewOsFs()}
|
||||
// Create tmp dir
|
||||
tmpDir, err := fs.TempDir(os.TempDir(), "util_file_test_")
|
||||
if err != nil {
|
||||
t.Fatal("Failed to test: failed to create temp dir.")
|
||||
}
|
||||
|
||||
// create tmp file
|
||||
tmpFile, err := fs.TempFile(tmpDir, "test_file_exists_")
|
||||
if err != nil {
|
||||
t.Fatal("Failed to test: failed to create temp file.")
|
||||
}
|
||||
|
||||
// create tmp sym link
|
||||
tmpSymlinkName := filepath.Join(tmpDir, "test_file_exists_sym_link")
|
||||
err = os.Symlink(tmpFile.Name(), tmpSymlinkName)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to test: failed to create sym link.")
|
||||
}
|
||||
|
||||
// create tmp sub dir
|
||||
tmpSubDir, err := fs.TempDir(tmpDir, "sub_")
|
||||
if err != nil {
|
||||
t.Fatal("Failed to test: failed to create temp sub dir.")
|
||||
}
|
||||
|
||||
// record the current dir
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to test: failed to get current dir.")
|
||||
}
|
||||
|
||||
// change the work dir to temp dir
|
||||
err = os.Chdir(tmpDir)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to test: failed to change work dir.")
|
||||
}
|
||||
|
||||
// recover test environment
|
||||
defer func() {
|
||||
os.Chdir(currentDir)
|
||||
os.RemoveAll(tmpDir)
|
||||
}()
|
||||
|
||||
t.Run("TestExists", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fileName string
|
||||
expectedError bool
|
||||
expectedValue bool
|
||||
}{
|
||||
{"file_not_exists", filepath.Join(tmpDir, "file_not_exist_case"), false, false},
|
||||
{"file_exists", tmpFile.Name(), false, true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
realValued, realError := Exists(CheckFollowSymlink, test.fileName)
|
||||
if test.expectedError {
|
||||
assert.Errorf(t, realError, "Failed to test with '%s': %s", test.fileName, test.name)
|
||||
} else {
|
||||
assert.EqualValuesf(t, test.expectedValue, realValued, "Failed to test with '%s': %s", test.fileName, test.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TestFileOrSymlinkExists", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fileName string
|
||||
expectedError bool
|
||||
expectedValue bool
|
||||
}{
|
||||
{"file_not_exists", filepath.Join(tmpDir, "file_not_exist_case"), false, false},
|
||||
{"file_exists", tmpFile.Name(), false, true},
|
||||
{"symlink_exists", tmpSymlinkName, false, true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
realValued, realError := Exists(CheckSymlinkOnly, test.fileName)
|
||||
if test.expectedError {
|
||||
assert.Errorf(t, realError, "Failed to test with '%s': %s", test.fileName, test.name)
|
||||
} else {
|
||||
assert.EqualValuesf(t, test.expectedValue, realValued, "Failed to test with '%s': %s", test.fileName, test.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TestReadDirNoStat", func(t *testing.T) {
|
||||
_, tmpFileSimpleName := filepath.Split(tmpFile.Name())
|
||||
_, tmpSymlinkSimpleName := filepath.Split(tmpSymlinkName)
|
||||
_, tmpSubDirSimpleName := filepath.Split(tmpSubDir)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
dirName string
|
||||
expectedError bool
|
||||
expectedValue []string
|
||||
}{
|
||||
{"dir_not_exists", filepath.Join(tmpDir, "file_not_exist_case"), true, []string{}},
|
||||
{"dir_is_empty", "", false, []string{tmpFileSimpleName, tmpSymlinkSimpleName, tmpSubDirSimpleName}},
|
||||
{"dir_exists", tmpDir, false, []string{tmpFileSimpleName, tmpSymlinkSimpleName, tmpSubDirSimpleName}},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
realValued, realError := ReadDirNoStat(test.dirName)
|
||||
|
||||
// execute sort action before compare
|
||||
sort.Strings(realValued)
|
||||
sort.Strings(test.expectedValue)
|
||||
|
||||
if test.expectedError {
|
||||
assert.Errorf(t, realError, "Failed to test with '%s': %s", test.dirName, test.name)
|
||||
} else {
|
||||
assert.EqualValuesf(t, test.expectedValue, realValued, "Failed to test with '%s': %s", test.dirName, test.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
10
vendor/k8s.io/utils/pointer/OWNERS
generated
vendored
10
vendor/k8s.io/utils/pointer/OWNERS
generated
vendored
@@ -1,10 +0,0 @@
|
||||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
approvers:
|
||||
- apelisse
|
||||
- stewart-yu
|
||||
- thockin
|
||||
reviewers:
|
||||
- apelisse
|
||||
- stewart-yu
|
||||
- thockin
|
||||
86
vendor/k8s.io/utils/pointer/pointer.go
generated
vendored
86
vendor/k8s.io/utils/pointer/pointer.go
generated
vendored
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
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 pointer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// AllPtrFieldsNil tests whether all pointer fields in a struct are nil. This is useful when,
|
||||
// for example, an API struct is handled by plugins which need to distinguish
|
||||
// "no plugin accepted this spec" from "this spec is empty".
|
||||
//
|
||||
// This function is only valid for structs and pointers to structs. Any other
|
||||
// type will cause a panic. Passing a typed nil pointer will return true.
|
||||
func AllPtrFieldsNil(obj interface{}) bool {
|
||||
v := reflect.ValueOf(obj)
|
||||
if !v.IsValid() {
|
||||
panic(fmt.Sprintf("reflect.ValueOf() produced a non-valid Value for %#v", obj))
|
||||
}
|
||||
if v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
return true
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
if v.Field(i).Kind() == reflect.Ptr && !v.Field(i).IsNil() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Int32Ptr returns a pointer to an int32
|
||||
func Int32Ptr(i int32) *int32 {
|
||||
return &i
|
||||
}
|
||||
|
||||
// Int64Ptr returns a pointer to an int64
|
||||
func Int64Ptr(i int64) *int64 {
|
||||
return &i
|
||||
}
|
||||
|
||||
// Int32PtrDerefOr dereference the int32 ptr and returns it if not nil,
|
||||
// else returns def.
|
||||
func Int32PtrDerefOr(ptr *int32, def int32) int32 {
|
||||
if ptr != nil {
|
||||
return *ptr
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// BoolPtr returns a pointer to a bool
|
||||
func BoolPtr(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
// StringPtr returns a pointer to the passed string.
|
||||
func StringPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
// Float32Ptr returns a pointer to the passed float32.
|
||||
func Float32Ptr(i float32) *float32 {
|
||||
return &i
|
||||
}
|
||||
|
||||
// Float64Ptr returns a pointer to the passed float64.
|
||||
func Float64Ptr(i float64) *float64 {
|
||||
return &i
|
||||
}
|
||||
72
vendor/k8s.io/utils/pointer/pointer_test.go
generated
vendored
72
vendor/k8s.io/utils/pointer/pointer_test.go
generated
vendored
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
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 pointer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAllPtrFieldsNil(t *testing.T) {
|
||||
testCases := []struct {
|
||||
obj interface{}
|
||||
expected bool
|
||||
}{
|
||||
{struct{}{}, true},
|
||||
{struct{ Foo int }{12345}, true},
|
||||
{&struct{ Foo int }{12345}, true},
|
||||
{struct{ Foo *int }{nil}, true},
|
||||
{&struct{ Foo *int }{nil}, true},
|
||||
{struct {
|
||||
Foo int
|
||||
Bar *int
|
||||
}{12345, nil}, true},
|
||||
{&struct {
|
||||
Foo int
|
||||
Bar *int
|
||||
}{12345, nil}, true},
|
||||
{struct {
|
||||
Foo *int
|
||||
Bar *int
|
||||
}{nil, nil}, true},
|
||||
{&struct {
|
||||
Foo *int
|
||||
Bar *int
|
||||
}{nil, nil}, true},
|
||||
{struct{ Foo *int }{new(int)}, false},
|
||||
{&struct{ Foo *int }{new(int)}, false},
|
||||
{struct {
|
||||
Foo *int
|
||||
Bar *int
|
||||
}{nil, new(int)}, false},
|
||||
{&struct {
|
||||
Foo *int
|
||||
Bar *int
|
||||
}{nil, new(int)}, false},
|
||||
{(*struct{})(nil), true},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
name := fmt.Sprintf("case[%d]", i)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
actualErr := AllPtrFieldsNil(tc.obj)
|
||||
if !reflect.DeepEqual(tc.expected, actualErr) {
|
||||
t.Errorf("%s: expected %t, got %t", name, tc.expected, !tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
30
vendor/k8s.io/utils/semantic/deep_equal.go
generated
vendored
30
vendor/k8s.io/utils/semantic/deep_equal.go
generated
vendored
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
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 semantic
|
||||
|
||||
import (
|
||||
"k8s.io/utils/third_party/forked/golang/reflect"
|
||||
)
|
||||
|
||||
// Equalities is a map from type to a function comparing two values of
|
||||
// that type.
|
||||
type Equalities = reflect.Equalities
|
||||
|
||||
// EqualitiesOrDie adds the given funcs and panics on any error.
|
||||
func EqualitiesOrDie(funcs ...interface{}) Equalities {
|
||||
return reflect.EqualitiesOrDie(funcs...)
|
||||
}
|
||||
31
vendor/k8s.io/utils/semantic/deep_equal_test.go
generated
vendored
31
vendor/k8s.io/utils/semantic/deep_equal_test.go
generated
vendored
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
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 semantic
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var mod2Equal = EqualitiesOrDie(func(a, b int) bool {
|
||||
return a%2 == b%2
|
||||
})
|
||||
|
||||
func TestEqualities(t *testing.T) {
|
||||
if !mod2Equal.DeepEqual(3, 5) {
|
||||
t.Error("expected 3 and 5 to be equal mod 2")
|
||||
}
|
||||
}
|
||||
36
vendor/k8s.io/utils/strings/escape.go
generated
vendored
36
vendor/k8s.io/utils/strings/escape.go
generated
vendored
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
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 strings
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// EscapeQualifiedName converts a plugin name, which might contain a / into a
|
||||
// string that is safe to use on-disk. This assumes that the input has already
|
||||
// been validates as a qualified name. we use "~" rather than ":" here in case
|
||||
// we ever use a filesystem that doesn't allow ":".
|
||||
func EscapeQualifiedName(in string) string {
|
||||
return strings.Replace(in, "/", "~", -1)
|
||||
}
|
||||
|
||||
// UnescapeQualifiedName converts an escaped plugin name (as per EscapeQualifiedName)
|
||||
// back to its normal form. This assumes that the input has already been
|
||||
// validates as a qualified name.
|
||||
func UnescapeQualifiedName(in string) string {
|
||||
return strings.Replace(in, "~", "/", -1)
|
||||
}
|
||||
42
vendor/k8s.io/utils/strings/escape_test.go
generated
vendored
42
vendor/k8s.io/utils/strings/escape_test.go
generated
vendored
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
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 strings
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEscapeQualifiedNameForDisk(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{"kubernetes.io/blah", "kubernetes.io~blah"},
|
||||
{"blah/blerg/borg", "blah~blerg~borg"},
|
||||
{"kubernetes.io", "kubernetes.io"},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
escapee := EscapeQualifiedName(tc.input)
|
||||
if escapee != tc.output {
|
||||
t.Errorf("case[%d]: expected (%q), got (%q)", i, tc.output, escapee)
|
||||
}
|
||||
original := UnescapeQualifiedName(escapee)
|
||||
if original != tc.input {
|
||||
t.Errorf("case[%d]: expected (%q), got (%q)", i, tc.input, original)
|
||||
}
|
||||
}
|
||||
}
|
||||
64
vendor/k8s.io/utils/strings/line_delimiter.go
generated
vendored
64
vendor/k8s.io/utils/strings/line_delimiter.go
generated
vendored
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
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 strings
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// LineDelimiter is a filter that will split input on lines
|
||||
// and bracket each line with the delimiter string.
|
||||
type LineDelimiter struct {
|
||||
output io.Writer
|
||||
delimiter []byte
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
// NewLineDelimiter allocates a new io.Writer that will split input on lines
|
||||
// and bracket each line with the delimiter string. This can be useful in
|
||||
// output tests where it is difficult to see and test trailing whitespace.
|
||||
func NewLineDelimiter(output io.Writer, delimiter string) *LineDelimiter {
|
||||
return &LineDelimiter{output: output, delimiter: []byte(delimiter)}
|
||||
}
|
||||
|
||||
// Write writes buf to the LineDelimiter ld. The only errors returned are ones
|
||||
// encountered while writing to the underlying output stream.
|
||||
func (ld *LineDelimiter) Write(buf []byte) (n int, err error) {
|
||||
return ld.buf.Write(buf)
|
||||
}
|
||||
|
||||
// Flush all lines up until now. This will assume insert a linebreak at the current point of the stream.
|
||||
func (ld *LineDelimiter) Flush() (err error) {
|
||||
lines := strings.Split(ld.buf.String(), "\n")
|
||||
for _, line := range lines {
|
||||
if _, err = ld.output.Write(ld.delimiter); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = ld.output.Write([]byte(line)); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = ld.output.Write(ld.delimiter); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = ld.output.Write([]byte("\n")); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
40
vendor/k8s.io/utils/strings/line_delimiter_test.go
generated
vendored
40
vendor/k8s.io/utils/strings/line_delimiter_test.go
generated
vendored
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
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 strings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func Example_trailingNewline() {
|
||||
ld := NewLineDelimiter(os.Stdout, "|")
|
||||
defer ld.Flush()
|
||||
fmt.Fprint(ld, " Hello \n World \n")
|
||||
// Output:
|
||||
// | Hello |
|
||||
// | World |
|
||||
// ||
|
||||
}
|
||||
func Example_noTrailingNewline() {
|
||||
ld := NewLineDelimiter(os.Stdout, "|")
|
||||
defer ld.Flush()
|
||||
fmt.Fprint(ld, " Hello \n World ")
|
||||
// Output:
|
||||
// | Hello |
|
||||
// | World |
|
||||
}
|
||||
46
vendor/k8s.io/utils/strings/strings.go
generated
vendored
46
vendor/k8s.io/utils/strings/strings.go
generated
vendored
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
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 strings
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SplitQualifiedName Splits a fully qualified name and returns its namespace and name.
|
||||
// Assumes that the input 'str' has been validated.
|
||||
func SplitQualifiedName(str string) (string, string) {
|
||||
parts := strings.Split(str, "/")
|
||||
if len(parts) < 2 {
|
||||
return "", str
|
||||
}
|
||||
return parts[0], parts[1]
|
||||
}
|
||||
|
||||
// JoinQualifiedName joins 'namespace' and 'name' and returns a fully qualified name
|
||||
// Assumes that the input is valid.
|
||||
func JoinQualifiedName(namespace, name string) string {
|
||||
return path.Join(namespace, name)
|
||||
}
|
||||
|
||||
// ShortenString returns the first N slice of a string.
|
||||
func ShortenString(str string, n int) string {
|
||||
if len(str) <= n {
|
||||
return str
|
||||
}
|
||||
return str[:n]
|
||||
}
|
||||
73
vendor/k8s.io/utils/strings/strings_test.go
generated
vendored
73
vendor/k8s.io/utils/strings/strings_test.go
generated
vendored
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
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 strings
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSplitQualifiedName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output []string
|
||||
}{
|
||||
{"kubernetes.io/blah", []string{"kubernetes.io", "blah"}},
|
||||
{"blah", []string{"", "blah"}},
|
||||
{"kubernetes.io/blah/blah", []string{"kubernetes.io", "blah"}},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
namespace, name := SplitQualifiedName(tc.input)
|
||||
if namespace != tc.output[0] || name != tc.output[1] {
|
||||
t.Errorf("case[%d]: expected (%q, %q), got (%q, %q)", i, tc.output[0], tc.output[1], namespace, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinQualifiedName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input []string
|
||||
output string
|
||||
}{
|
||||
{[]string{"kubernetes.io", "blah"}, "kubernetes.io/blah"},
|
||||
{[]string{"blah", ""}, "blah"},
|
||||
{[]string{"kubernetes.io", "blah"}, "kubernetes.io/blah"},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
res := JoinQualifiedName(tc.input[0], tc.input[1])
|
||||
if res != tc.output {
|
||||
t.Errorf("case[%d]: expected %q, got %q", i, tc.output, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestShortenString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
outLen int
|
||||
output string
|
||||
}{
|
||||
{"kubernetes.io", 5, "kuber"},
|
||||
{"blah", 34, "blah"},
|
||||
{"kubernetes.io", 13, "kubernetes.io"},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
res := ShortenString(tc.input, tc.outLen)
|
||||
if res != tc.output {
|
||||
t.Errorf("case[%d]: expected %q, got %q", i, tc.output, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
70
vendor/k8s.io/utils/temp/dir.go
generated
vendored
70
vendor/k8s.io/utils/temp/dir.go
generated
vendored
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
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 temp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Directory is an interface to a temporary directory, in which you can
|
||||
// create new files.
|
||||
type Directory interface {
|
||||
// NewFile creates a new file in that directory. Calling NewFile
|
||||
// with the same filename twice will result in an error.
|
||||
NewFile(name string) (io.WriteCloser, error)
|
||||
// Delete removes the directory and its content.
|
||||
Delete() error
|
||||
}
|
||||
|
||||
// Dir is wrapping an temporary directory on disk.
|
||||
type Dir struct {
|
||||
// Name is the name (full path) of the created directory.
|
||||
Name string
|
||||
}
|
||||
|
||||
var _ Directory = &Dir{}
|
||||
|
||||
// CreateTempDir returns a new Directory wrapping a temporary directory
|
||||
// on disk.
|
||||
func CreateTempDir(prefix string) (*Dir, error) {
|
||||
name, err := ioutil.TempDir("", fmt.Sprintf("%s-", prefix))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Dir{
|
||||
Name: name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewFile creates a new file in the specified directory.
|
||||
func (d *Dir) NewFile(name string) (io.WriteCloser, error) {
|
||||
return os.OpenFile(
|
||||
filepath.Join(d.Name, name),
|
||||
os.O_WRONLY|os.O_CREATE|os.O_TRUNC|os.O_EXCL,
|
||||
0700,
|
||||
)
|
||||
}
|
||||
|
||||
// Delete the underlying directory, and all of its content.
|
||||
func (d *Dir) Delete() error {
|
||||
return os.RemoveAll(d.Name)
|
||||
}
|
||||
90
vendor/k8s.io/utils/temp/dir_test.go
generated
vendored
90
vendor/k8s.io/utils/temp/dir_test.go
generated
vendored
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
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 temp
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTempDir(t *testing.T) {
|
||||
dir, err := CreateTempDir("prefix")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Delete the directory no matter what.
|
||||
defer dir.Delete()
|
||||
|
||||
// Make sure we have created the dir, with the proper name
|
||||
_, err = os.Stat(dir.Name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.HasPrefix(filepath.Base(dir.Name), "prefix") {
|
||||
t.Fatalf(`Directory doesn't start with "prefix": %q`,
|
||||
dir.Name)
|
||||
}
|
||||
|
||||
// Verify that the directory is empty
|
||||
entries, err := ioutil.ReadDir(dir.Name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(entries) != 0 {
|
||||
t.Fatalf("Directory should be empty, has %d elements",
|
||||
len(entries))
|
||||
}
|
||||
|
||||
// Create a couple of files
|
||||
_, err = dir.NewFile("ONE")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = dir.NewFile("TWO")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// We can't create the same file twice
|
||||
_, err = dir.NewFile("TWO")
|
||||
if err == nil {
|
||||
t.Fatal("NewFile should fail to create the same file twice")
|
||||
}
|
||||
|
||||
// We have created only two files
|
||||
entries, err = ioutil.ReadDir(dir.Name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(entries) != 2 {
|
||||
t.Fatalf("ReadDir should have two elements, has %d elements",
|
||||
len(entries))
|
||||
}
|
||||
|
||||
// Verify that deletion works
|
||||
err = dir.Delete()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = os.Stat(dir.Name)
|
||||
if err == nil {
|
||||
t.Fatal("Directory should be gone, still present.")
|
||||
}
|
||||
}
|
||||
19
vendor/k8s.io/utils/temp/doc.go
generated
vendored
19
vendor/k8s.io/utils/temp/doc.go
generated
vendored
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
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 temp provides an interface to handle temporary files and
|
||||
// directories.
|
||||
package temp // import "k8s.io/utils/temp"
|
||||
66
vendor/k8s.io/utils/temp/temptest/dir.go
generated
vendored
66
vendor/k8s.io/utils/temp/temptest/dir.go
generated
vendored
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
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 temptest
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"k8s.io/utils/temp"
|
||||
)
|
||||
|
||||
// FakeDir implements a Directory that is not backed on the
|
||||
// filesystem. This is useful for testing since the created "files" are
|
||||
// simple bytes.Buffer that can be inspected.
|
||||
type FakeDir struct {
|
||||
Files map[string]*FakeFile
|
||||
Deleted bool
|
||||
}
|
||||
|
||||
var _ temp.Directory = &FakeDir{}
|
||||
|
||||
// NewFile returns a new FakeFile if the filename doesn't exist already.
|
||||
// This function will fail if the directory has already been deleted.
|
||||
func (d *FakeDir) NewFile(name string) (io.WriteCloser, error) {
|
||||
if d.Deleted {
|
||||
return nil, errors.New("can't create file in deleted FakeDir")
|
||||
}
|
||||
if d.Files == nil {
|
||||
d.Files = map[string]*FakeFile{}
|
||||
}
|
||||
f := d.Files[name]
|
||||
if f != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"FakeDir already has file named %q",
|
||||
name,
|
||||
)
|
||||
}
|
||||
f = &FakeFile{}
|
||||
d.Files[name] = f
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// Delete doesn't remove anything, but records that the directory has
|
||||
// been deleted.
|
||||
func (d *FakeDir) Delete() error {
|
||||
if d.Deleted {
|
||||
return errors.New("failed to re-delete FakeDir")
|
||||
}
|
||||
d.Deleted = true
|
||||
return nil
|
||||
}
|
||||
67
vendor/k8s.io/utils/temp/temptest/dir_test.go
generated
vendored
67
vendor/k8s.io/utils/temp/temptest/dir_test.go
generated
vendored
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
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 temptest
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFakeDir(t *testing.T) {
|
||||
d := &FakeDir{}
|
||||
|
||||
f, err := d.NewFile("ONE")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
n, err := io.WriteString(f, "Bonjour!")
|
||||
if n != 8 || err != nil {
|
||||
t.Fatalf(
|
||||
`WriteString(f, "Bonjour!") = (%v, %v), expected (%v, %v)`,
|
||||
n, err,
|
||||
0, nil,
|
||||
)
|
||||
}
|
||||
if got := d.Files["ONE"].Buffer.String(); got != "Bonjour!" {
|
||||
t.Fatalf(`file content is %q, expected "Bonjour!"`, got)
|
||||
}
|
||||
|
||||
f, err = d.NewFile("ONE")
|
||||
if err == nil {
|
||||
t.Fatal("Same file could be created twice.")
|
||||
}
|
||||
|
||||
err = d.Delete()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = d.Delete()
|
||||
if err == nil {
|
||||
t.Fatal("FakeDir could be deleted twice.")
|
||||
}
|
||||
|
||||
f, err = d.NewFile("TWO")
|
||||
if err == nil {
|
||||
t.Fatal("NewFile could be created in deleted dir")
|
||||
}
|
||||
|
||||
if !d.Deleted {
|
||||
t.Fatal("FakeDir should be deleted.")
|
||||
}
|
||||
}
|
||||
19
vendor/k8s.io/utils/temp/temptest/doc.go
generated
vendored
19
vendor/k8s.io/utils/temp/temptest/doc.go
generated
vendored
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
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 temptest provides utilities for testing temp
|
||||
// files/directories testing.
|
||||
package temptest
|
||||
57
vendor/k8s.io/utils/temp/temptest/example_test.go
generated
vendored
57
vendor/k8s.io/utils/temp/temptest/example_test.go
generated
vendored
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
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 temptest
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"k8s.io/utils/temp"
|
||||
)
|
||||
|
||||
func TestedCode(dir temp.Directory) error {
|
||||
f, err := dir.NewFile("filename")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.WriteString(f, "Bonjour!")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dir.Delete()
|
||||
}
|
||||
|
||||
func Example() {
|
||||
dir := FakeDir{}
|
||||
|
||||
err := TestedCode(&dir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if dir.Deleted == false {
|
||||
panic(errors.New("Directory should have been deleted"))
|
||||
}
|
||||
|
||||
if dir.Files["filename"] == nil {
|
||||
panic(errors.New(`"filename" should have been created`))
|
||||
}
|
||||
|
||||
fmt.Println(dir.Files["filename"].Buffer.String())
|
||||
// Output: Bonjour!
|
||||
}
|
||||
52
vendor/k8s.io/utils/temp/temptest/file.go
generated
vendored
52
vendor/k8s.io/utils/temp/temptest/file.go
generated
vendored
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
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 temptest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// FakeFile is an implementation of a WriteCloser, that records what has
|
||||
// been written in the file (in a bytes.Buffer) and if the file has been
|
||||
// closed.
|
||||
type FakeFile struct {
|
||||
Buffer bytes.Buffer
|
||||
Closed bool
|
||||
}
|
||||
|
||||
var _ io.WriteCloser = &FakeFile{}
|
||||
|
||||
// Write appends the contents of p to the Buffer. If the file has
|
||||
// already been closed, an error is returned.
|
||||
func (f *FakeFile) Write(p []byte) (n int, err error) {
|
||||
if f.Closed {
|
||||
return 0, errors.New("can't write to closed FakeFile")
|
||||
}
|
||||
return f.Buffer.Write(p)
|
||||
}
|
||||
|
||||
// Close records that the file has been closed. If the file has already
|
||||
// been closed, an error is returned.
|
||||
func (f *FakeFile) Close() error {
|
||||
if f.Closed {
|
||||
return errors.New("FakeFile was closed multiple times")
|
||||
}
|
||||
f.Closed = true
|
||||
return nil
|
||||
}
|
||||
56
vendor/k8s.io/utils/temp/temptest/file_test.go
generated
vendored
56
vendor/k8s.io/utils/temp/temptest/file_test.go
generated
vendored
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
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 temptest
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFakeFile(t *testing.T) {
|
||||
f := &FakeFile{}
|
||||
|
||||
n, err := io.WriteString(f, "Bonjour!")
|
||||
if n != 8 || err != nil {
|
||||
t.Fatalf(
|
||||
`WriteString(f, "Bonjour!") = (%v, %v), expected (%v, %v)`,
|
||||
n, err,
|
||||
8, nil,
|
||||
)
|
||||
}
|
||||
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// File can't be closed twice.
|
||||
err = f.Close()
|
||||
if err == nil {
|
||||
t.Fatal("FakeFile could be closed twice")
|
||||
}
|
||||
|
||||
// File is not writable after close.
|
||||
n, err = io.WriteString(f, "Bonjour!")
|
||||
if n != 0 || err == nil {
|
||||
t.Fatalf(
|
||||
`WriteString(f, "Bonjour!") = (%v, %v), expected (%v, %v)`,
|
||||
n, err,
|
||||
0, "non-nil",
|
||||
)
|
||||
}
|
||||
}
|
||||
27
vendor/k8s.io/utils/third_party/forked/golang/LICENSE
generated
vendored
27
vendor/k8s.io/utils/third_party/forked/golang/LICENSE
generated
vendored
@@ -1,27 +0,0 @@
|
||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
22
vendor/k8s.io/utils/third_party/forked/golang/PATENTS
generated
vendored
22
vendor/k8s.io/utils/third_party/forked/golang/PATENTS
generated
vendored
@@ -1,22 +0,0 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google 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,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
6
vendor/k8s.io/utils/third_party/forked/golang/reflect/README.md
generated
vendored
6
vendor/k8s.io/utils/third_party/forked/golang/reflect/README.md
generated
vendored
@@ -1,6 +0,0 @@
|
||||
This was originally forked from https://github.com/golang/go/blob/master/src/reflect/deepequal.go in order to
|
||||
|
||||
- consider empty lists and empty maps equal to their nil counterparts
|
||||
- add a `AddFuncs` mechanism to add custom equality funcs for specific types.
|
||||
|
||||
Meanwhile it has diverged quite a lot while still following the original algorithm at the core though.
|
||||
398
vendor/k8s.io/utils/third_party/forked/golang/reflect/deep_equal.go
generated
vendored
398
vendor/k8s.io/utils/third_party/forked/golang/reflect/deep_equal.go
generated
vendored
@@ -1,398 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package reflect is a fork of go's standard library reflection package, which
|
||||
// allows for deep equal with equality functions defined.
|
||||
package reflect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Equalities is a map from type to a function comparing two values of
|
||||
// that type.
|
||||
type Equalities map[reflect.Type]reflect.Value
|
||||
|
||||
// EqualitiesOrDie adds the given funcs and panics on any error.
|
||||
func EqualitiesOrDie(funcs ...interface{}) Equalities {
|
||||
e := Equalities{}
|
||||
if err := e.AddFuncs(funcs...); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// AddFuncs is a shortcut for multiple calls to AddFunc.
|
||||
func (e Equalities) AddFuncs(funcs ...interface{}) error {
|
||||
for _, f := range funcs {
|
||||
if err := e.AddFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddFunc uses func as an equality function: it must take
|
||||
// two parameters of the same type, and return a boolean.
|
||||
func (e Equalities) AddFunc(eqFunc interface{}) error {
|
||||
fv := reflect.ValueOf(eqFunc)
|
||||
ft := fv.Type()
|
||||
if ft.Kind() != reflect.Func {
|
||||
return fmt.Errorf("expected func, got: %v", ft)
|
||||
}
|
||||
if ft.NumIn() != 2 {
|
||||
return fmt.Errorf("expected two 'in' params, got: %v", ft)
|
||||
}
|
||||
if ft.NumOut() != 1 {
|
||||
return fmt.Errorf("expected one 'out' param, got: %v", ft)
|
||||
}
|
||||
if ft.In(0) != ft.In(1) {
|
||||
return fmt.Errorf("expected arg 1 and 2 to have same type, but got %v", ft)
|
||||
}
|
||||
var forReturnType bool
|
||||
boolType := reflect.TypeOf(forReturnType)
|
||||
if ft.Out(0) != boolType {
|
||||
return fmt.Errorf("expected bool return, got: %v", ft)
|
||||
}
|
||||
e[ft.In(0)] = fv
|
||||
return nil
|
||||
}
|
||||
|
||||
// Below here is forked from go's reflect/deepequal.go
|
||||
|
||||
// During deepValueEqual, must keep track of checks that are
|
||||
// in progress. The comparison algorithm assumes that all
|
||||
// checks in progress are true when it reencounters them.
|
||||
// Visited comparisons are stored in a map indexed by visit.
|
||||
type visit struct {
|
||||
a1 uintptr
|
||||
a2 uintptr
|
||||
typ reflect.Type
|
||||
}
|
||||
|
||||
// unexportedTypePanic is thrown when you use this DeepEqual on something that has an
|
||||
// unexported type. It indicates a programmer error, so should not occur at runtime,
|
||||
// which is why it's not public and thus impossible to catch.
|
||||
type unexportedTypePanic []reflect.Type
|
||||
|
||||
func (u unexportedTypePanic) Error() string { return u.String() }
|
||||
func (u unexportedTypePanic) String() string {
|
||||
strs := make([]string, len(u))
|
||||
for i, t := range u {
|
||||
strs[i] = fmt.Sprintf("%v", t)
|
||||
}
|
||||
return "an unexported field was encountered, nested like this: " + strings.Join(strs, " -> ")
|
||||
}
|
||||
|
||||
func makeUsefulPanic(v reflect.Value) {
|
||||
if x := recover(); x != nil {
|
||||
if u, ok := x.(unexportedTypePanic); ok {
|
||||
u = append(unexportedTypePanic{v.Type()}, u...)
|
||||
x = u
|
||||
}
|
||||
panic(x)
|
||||
}
|
||||
}
|
||||
|
||||
// deepValueEqual tests for deep equality using reflected types. The map argument tracks
|
||||
// comparisons that have already been seen, which allows short circuiting on
|
||||
// recursive types.
|
||||
func (e Equalities) deepValueEqual(v1, v2 reflect.Value, visited map[visit]bool, depth int) bool {
|
||||
defer makeUsefulPanic(v1)
|
||||
|
||||
if !v1.IsValid() || !v2.IsValid() {
|
||||
return v1.IsValid() == v2.IsValid()
|
||||
}
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
if fv, ok := e[v1.Type()]; ok {
|
||||
return fv.Call([]reflect.Value{v1, v2})[0].Bool()
|
||||
}
|
||||
if v1.CanAddr() {
|
||||
if fv, ok := e[v1.Addr().Type()]; ok {
|
||||
return fv.Call([]reflect.Value{v1.Addr(), v2.Addr()})[0].Bool()
|
||||
}
|
||||
}
|
||||
|
||||
hard := func(k reflect.Kind) bool {
|
||||
switch k {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) {
|
||||
addr1 := v1.UnsafeAddr()
|
||||
addr2 := v2.UnsafeAddr()
|
||||
if addr1 > addr2 {
|
||||
// Canonicalize order to reduce number of entries in visited.
|
||||
addr1, addr2 = addr2, addr1
|
||||
}
|
||||
|
||||
// Short circuit if references are identical ...
|
||||
if addr1 == addr2 {
|
||||
return true
|
||||
}
|
||||
|
||||
// ... or already seen
|
||||
typ := v1.Type()
|
||||
v := visit{addr1, addr2, typ}
|
||||
if visited[v] {
|
||||
return true
|
||||
}
|
||||
|
||||
// Remember for later.
|
||||
visited[v] = true
|
||||
}
|
||||
|
||||
switch v1.Kind() {
|
||||
case reflect.Array:
|
||||
// We don't need to check length here because length is part of
|
||||
// an array's type, which has already been filtered for.
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !e.deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Slice:
|
||||
if (v1.IsNil() || v1.Len() == 0) != (v2.IsNil() || v2.Len() == 0) {
|
||||
return false
|
||||
}
|
||||
if v1.IsNil() || v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.Len() != v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !e.deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Interface:
|
||||
if v1.IsNil() || v2.IsNil() {
|
||||
return v1.IsNil() == v2.IsNil()
|
||||
}
|
||||
return e.deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
|
||||
case reflect.Ptr:
|
||||
return e.deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
|
||||
case reflect.Struct:
|
||||
for i, n := 0, v1.NumField(); i < n; i++ {
|
||||
if !e.deepValueEqual(v1.Field(i), v2.Field(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Map:
|
||||
if (v1.IsNil() || v1.Len() == 0) != (v2.IsNil() || v2.Len() == 0) {
|
||||
return false
|
||||
}
|
||||
if v1.IsNil() || v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.Len() != v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for _, k := range v1.MapKeys() {
|
||||
if !e.deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Func:
|
||||
if v1.IsNil() && v2.IsNil() {
|
||||
return true
|
||||
}
|
||||
// Can't do better than this:
|
||||
return false
|
||||
default:
|
||||
// Normal equality suffices
|
||||
if !v1.CanInterface() || !v2.CanInterface() {
|
||||
panic(unexportedTypePanic{})
|
||||
}
|
||||
return v1.Interface() == v2.Interface()
|
||||
}
|
||||
}
|
||||
|
||||
// DeepEqual is like reflect.DeepEqual, but focused on semantic equality
|
||||
// instead of memory equality.
|
||||
//
|
||||
// It will use e's equality functions if it finds types that match.
|
||||
//
|
||||
// An empty slice *is* equal to a nil slice for our purposes; same for maps.
|
||||
//
|
||||
// Unexported field members cannot be compared and will cause an imformative panic; you must add an Equality
|
||||
// function for these types.
|
||||
func (e Equalities) DeepEqual(a1, a2 interface{}) bool {
|
||||
if a1 == nil || a2 == nil {
|
||||
return a1 == a2
|
||||
}
|
||||
v1 := reflect.ValueOf(a1)
|
||||
v2 := reflect.ValueOf(a2)
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
return e.deepValueEqual(v1, v2, make(map[visit]bool), 0)
|
||||
}
|
||||
|
||||
func (e Equalities) deepValueDerive(v1, v2 reflect.Value, visited map[visit]bool, depth int) bool {
|
||||
defer makeUsefulPanic(v1)
|
||||
|
||||
if !v1.IsValid() || !v2.IsValid() {
|
||||
return v1.IsValid() == v2.IsValid()
|
||||
}
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
if fv, ok := e[v1.Type()]; ok {
|
||||
return fv.Call([]reflect.Value{v1, v2})[0].Bool()
|
||||
}
|
||||
if v1.CanAddr() {
|
||||
if fv, ok := e[v1.Addr().Type()]; ok {
|
||||
return fv.Call([]reflect.Value{v1.Addr(), v2.Addr()})[0].Bool()
|
||||
}
|
||||
}
|
||||
|
||||
hard := func(k reflect.Kind) bool {
|
||||
switch k {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) {
|
||||
addr1 := v1.UnsafeAddr()
|
||||
addr2 := v2.UnsafeAddr()
|
||||
if addr1 > addr2 {
|
||||
// Canonicalize order to reduce number of entries in visited.
|
||||
addr1, addr2 = addr2, addr1
|
||||
}
|
||||
|
||||
// Short circuit if references are identical ...
|
||||
if addr1 == addr2 {
|
||||
return true
|
||||
}
|
||||
|
||||
// ... or already seen
|
||||
typ := v1.Type()
|
||||
v := visit{addr1, addr2, typ}
|
||||
if visited[v] {
|
||||
return true
|
||||
}
|
||||
|
||||
// Remember for later.
|
||||
visited[v] = true
|
||||
}
|
||||
|
||||
switch v1.Kind() {
|
||||
case reflect.Array:
|
||||
// We don't need to check length here because length is part of
|
||||
// an array's type, which has already been filtered for.
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !e.deepValueDerive(v1.Index(i), v2.Index(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Slice:
|
||||
if v1.IsNil() || v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.Len() > v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !e.deepValueDerive(v1.Index(i), v2.Index(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.String:
|
||||
if v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.Len() > v2.Len() {
|
||||
return false
|
||||
}
|
||||
return v1.String() == v2.String()
|
||||
case reflect.Interface:
|
||||
if v1.IsNil() {
|
||||
return true
|
||||
}
|
||||
return e.deepValueDerive(v1.Elem(), v2.Elem(), visited, depth+1)
|
||||
case reflect.Ptr:
|
||||
if v1.IsNil() {
|
||||
return true
|
||||
}
|
||||
return e.deepValueDerive(v1.Elem(), v2.Elem(), visited, depth+1)
|
||||
case reflect.Struct:
|
||||
for i, n := 0, v1.NumField(); i < n; i++ {
|
||||
if !e.deepValueDerive(v1.Field(i), v2.Field(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Map:
|
||||
if v1.IsNil() || v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.Len() > v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for _, k := range v1.MapKeys() {
|
||||
if !e.deepValueDerive(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Func:
|
||||
if v1.IsNil() && v2.IsNil() {
|
||||
return true
|
||||
}
|
||||
// Can't do better than this:
|
||||
return false
|
||||
default:
|
||||
// Normal equality suffices
|
||||
if !v1.CanInterface() || !v2.CanInterface() {
|
||||
panic(unexportedTypePanic{})
|
||||
}
|
||||
return v1.Interface() == v2.Interface()
|
||||
}
|
||||
}
|
||||
|
||||
// DeepDerivative is similar to DeepEqual except that unset fields in a1 are
|
||||
// ignored (not compared). This allows us to focus on the fields that matter to
|
||||
// the semantic comparison.
|
||||
//
|
||||
// The unset fields include a nil pointer and an empty string.
|
||||
func (e Equalities) DeepDerivative(a1, a2 interface{}) bool {
|
||||
if a1 == nil {
|
||||
return true
|
||||
}
|
||||
v1 := reflect.ValueOf(a1)
|
||||
v2 := reflect.ValueOf(a2)
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
return e.deepValueDerive(v1, v2, make(map[visit]bool), 0)
|
||||
}
|
||||
137
vendor/k8s.io/utils/third_party/forked/golang/reflect/deep_equal_test.go
generated
vendored
137
vendor/k8s.io/utils/third_party/forked/golang/reflect/deep_equal_test.go
generated
vendored
@@ -1,137 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package reflect
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEqualities(t *testing.T) {
|
||||
e := Equalities{}
|
||||
type Bar struct {
|
||||
X int
|
||||
}
|
||||
type Baz struct {
|
||||
Y Bar
|
||||
}
|
||||
err := e.AddFuncs(
|
||||
func(a, b int) bool {
|
||||
return a+1 == b
|
||||
},
|
||||
func(a, b Bar) bool {
|
||||
return a.X*10 == b.X
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected: %v", err)
|
||||
}
|
||||
|
||||
type Foo struct {
|
||||
X int
|
||||
}
|
||||
|
||||
table := []struct {
|
||||
a, b interface{}
|
||||
equal bool
|
||||
}{
|
||||
{1, 2, true},
|
||||
{2, 1, false},
|
||||
{"foo", "fo", false},
|
||||
{"foo", "foo", true},
|
||||
{"foo", "foobar", false},
|
||||
{Foo{1}, Foo{2}, true},
|
||||
{Foo{2}, Foo{1}, false},
|
||||
{Bar{1}, Bar{10}, true},
|
||||
{&Bar{1}, &Bar{10}, true},
|
||||
{Baz{Bar{1}}, Baz{Bar{10}}, true},
|
||||
{[...]string{}, [...]string{"1", "2", "3"}, false},
|
||||
{[...]string{"1"}, [...]string{"1", "2", "3"}, false},
|
||||
{[...]string{"1", "2", "3"}, [...]string{}, false},
|
||||
{[...]string{"1", "2", "3"}, [...]string{"1", "2", "3"}, true},
|
||||
{map[string]int{"foo": 1}, map[string]int{}, false},
|
||||
{map[string]int{"foo": 1}, map[string]int{"foo": 2}, true},
|
||||
{map[string]int{"foo": 2}, map[string]int{"foo": 1}, false},
|
||||
{map[string]int{"foo": 1}, map[string]int{"foo": 2, "bar": 6}, false},
|
||||
{map[string]int{"foo": 1, "bar": 6}, map[string]int{"foo": 2}, false},
|
||||
{map[string]int{}, map[string]int(nil), true},
|
||||
{[]string(nil), []string(nil), true},
|
||||
{[]string{}, []string(nil), true},
|
||||
{[]string(nil), []string{}, true},
|
||||
{[]string{"1"}, []string(nil), false},
|
||||
{[]string{}, []string{"1", "2", "3"}, false},
|
||||
{[]string{"1"}, []string{"1", "2", "3"}, false},
|
||||
{[]string{"1", "2", "3"}, []string{}, false},
|
||||
}
|
||||
|
||||
for _, item := range table {
|
||||
if e, a := item.equal, e.DeepEqual(item.a, item.b); e != a {
|
||||
t.Errorf("Expected (%+v == %+v) == %v, but got %v", item.a, item.b, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDerivates(t *testing.T) {
|
||||
e := Equalities{}
|
||||
type Bar struct {
|
||||
X int
|
||||
}
|
||||
type Baz struct {
|
||||
Y Bar
|
||||
}
|
||||
err := e.AddFuncs(
|
||||
func(a, b int) bool {
|
||||
return a+1 == b
|
||||
},
|
||||
func(a, b Bar) bool {
|
||||
return a.X*10 == b.X
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected: %v", err)
|
||||
}
|
||||
|
||||
type Foo struct {
|
||||
X int
|
||||
}
|
||||
|
||||
table := []struct {
|
||||
a, b interface{}
|
||||
equal bool
|
||||
}{
|
||||
{1, 2, true},
|
||||
{2, 1, false},
|
||||
{"foo", "fo", false},
|
||||
{"foo", "foo", true},
|
||||
{"foo", "foobar", false},
|
||||
{Foo{1}, Foo{2}, true},
|
||||
{Foo{2}, Foo{1}, false},
|
||||
{Bar{1}, Bar{10}, true},
|
||||
{&Bar{1}, &Bar{10}, true},
|
||||
{Baz{Bar{1}}, Baz{Bar{10}}, true},
|
||||
{[...]string{}, [...]string{"1", "2", "3"}, false},
|
||||
{[...]string{"1"}, [...]string{"1", "2", "3"}, false},
|
||||
{[...]string{"1", "2", "3"}, [...]string{}, false},
|
||||
{[...]string{"1", "2", "3"}, [...]string{"1", "2", "3"}, true},
|
||||
{map[string]int{"foo": 1}, map[string]int{}, false},
|
||||
{map[string]int{"foo": 1}, map[string]int{"foo": 2}, true},
|
||||
{map[string]int{"foo": 2}, map[string]int{"foo": 1}, false},
|
||||
{map[string]int{"foo": 1}, map[string]int{"foo": 2, "bar": 6}, true},
|
||||
{map[string]int{"foo": 1, "bar": 6}, map[string]int{"foo": 2}, false},
|
||||
{map[string]int{}, map[string]int(nil), true},
|
||||
{[]string(nil), []string(nil), true},
|
||||
{[]string{}, []string(nil), true},
|
||||
{[]string(nil), []string{}, true},
|
||||
{[]string{"1"}, []string(nil), false},
|
||||
{[]string{}, []string{"1", "2", "3"}, true},
|
||||
{[]string{"1"}, []string{"1", "2", "3"}, true},
|
||||
{[]string{"1", "2", "3"}, []string{}, false},
|
||||
}
|
||||
|
||||
for _, item := range table {
|
||||
if e, a := item.equal, e.DeepDerivative(item.a, item.b); e != a {
|
||||
t.Errorf("Expected (%+v ~ %+v) == %v, but got %v", item.a, item.b, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
131
vendor/k8s.io/utils/trace/trace.go
generated
vendored
131
vendor/k8s.io/utils/trace/trace.go
generated
vendored
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
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 trace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// Field is a key value pair that provides additional details about the trace.
|
||||
type Field struct {
|
||||
Key string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func (f Field) format() string {
|
||||
return fmt.Sprintf("%s:%v", f.Key, f.Value)
|
||||
}
|
||||
|
||||
func writeFields(b *bytes.Buffer, l []Field) {
|
||||
for i, f := range l {
|
||||
b.WriteString(f.format())
|
||||
if i < len(l)-1 {
|
||||
b.WriteString(",")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type traceStep struct {
|
||||
stepTime time.Time
|
||||
msg string
|
||||
fields []Field
|
||||
}
|
||||
|
||||
// Trace keeps track of a set of "steps" and allows us to log a specific
|
||||
// step if it took longer than its share of the total allowed time
|
||||
type Trace struct {
|
||||
name string
|
||||
fields []Field
|
||||
startTime time.Time
|
||||
steps []traceStep
|
||||
}
|
||||
|
||||
// New creates a Trace with the specified name. The name identifies the operation to be traced. The
|
||||
// Fields add key value pairs to provide additional details about the trace, such as operation inputs.
|
||||
func New(name string, fields ...Field) *Trace {
|
||||
return &Trace{name: name, startTime: time.Now(), fields: fields}
|
||||
}
|
||||
|
||||
// Step adds a new step with a specific message. Call this at the end of an execution step to record
|
||||
// how long it took. The Fields add key value pairs to provide additional details about the trace
|
||||
// step.
|
||||
func (t *Trace) Step(msg string, fields ...Field) {
|
||||
if t.steps == nil {
|
||||
// traces almost always have less than 6 steps, do this to avoid more than a single allocation
|
||||
t.steps = make([]traceStep, 0, 6)
|
||||
}
|
||||
t.steps = append(t.steps, traceStep{stepTime: time.Now(), msg: msg, fields: fields})
|
||||
}
|
||||
|
||||
// Log is used to dump all the steps in the Trace
|
||||
func (t *Trace) Log() {
|
||||
// an explicit logging request should dump all the steps out at the higher level
|
||||
t.logWithStepThreshold(0)
|
||||
}
|
||||
|
||||
func (t *Trace) logWithStepThreshold(stepThreshold time.Duration) {
|
||||
var buffer bytes.Buffer
|
||||
tracenum := rand.Int31()
|
||||
endTime := time.Now()
|
||||
|
||||
totalTime := endTime.Sub(t.startTime)
|
||||
buffer.WriteString(fmt.Sprintf("Trace[%d]: %q ", tracenum, t.name))
|
||||
if len(t.fields) > 0 {
|
||||
writeFields(&buffer, t.fields)
|
||||
buffer.WriteString(" ")
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf("(started: %v) (total time: %v):\n", t.startTime, totalTime))
|
||||
lastStepTime := t.startTime
|
||||
for _, step := range t.steps {
|
||||
stepDuration := step.stepTime.Sub(lastStepTime)
|
||||
if stepThreshold == 0 || stepDuration > stepThreshold || klog.V(4) {
|
||||
buffer.WriteString(fmt.Sprintf("Trace[%d]: [%v] [%v] ", tracenum, step.stepTime.Sub(t.startTime), stepDuration))
|
||||
buffer.WriteString(step.msg)
|
||||
if len(step.fields) > 0 {
|
||||
buffer.WriteString(" ")
|
||||
writeFields(&buffer, step.fields)
|
||||
}
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
lastStepTime = step.stepTime
|
||||
}
|
||||
stepDuration := endTime.Sub(lastStepTime)
|
||||
if stepThreshold == 0 || stepDuration > stepThreshold || klog.V(4) {
|
||||
buffer.WriteString(fmt.Sprintf("Trace[%d]: [%v] [%v] END\n", tracenum, endTime.Sub(t.startTime), stepDuration))
|
||||
}
|
||||
|
||||
klog.Info(buffer.String())
|
||||
}
|
||||
|
||||
// LogIfLong is used to dump steps that took longer than its share
|
||||
func (t *Trace) LogIfLong(threshold time.Duration) {
|
||||
if time.Since(t.startTime) >= threshold {
|
||||
// if any step took more than it's share of the total allowed time, it deserves a higher log level
|
||||
stepThreshold := threshold / time.Duration(len(t.steps)+1)
|
||||
t.logWithStepThreshold(stepThreshold)
|
||||
}
|
||||
}
|
||||
|
||||
// TotalTime can be used to figure out how long it took since the Trace was created
|
||||
func (t *Trace) TotalTime() time.Duration {
|
||||
return time.Since(t.startTime)
|
||||
}
|
||||
248
vendor/k8s.io/utils/trace/trace_test.go
generated
vendored
248
vendor/k8s.io/utils/trace/trace_test.go
generated
vendored
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
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 trace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
func TestStep(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputString string
|
||||
expectedTrace *Trace
|
||||
}{
|
||||
{
|
||||
name: "When string is empty",
|
||||
inputString: "",
|
||||
expectedTrace: &Trace{
|
||||
steps: []traceStep{
|
||||
{stepTime: time.Now(), msg: ""},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "When string is not empty",
|
||||
inputString: "test2",
|
||||
expectedTrace: &Trace{
|
||||
steps: []traceStep{
|
||||
{stepTime: time.Now(), msg: "test2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sampleTrace := &Trace{}
|
||||
sampleTrace.Step(tt.inputString)
|
||||
if sampleTrace.steps[0].msg != tt.expectedTrace.steps[0].msg {
|
||||
t.Errorf("Expected %v \n Got %v \n", tt.expectedTrace, sampleTrace)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTotalTime(t *testing.T) {
|
||||
test := struct {
|
||||
name string
|
||||
inputTrace *Trace
|
||||
}{
|
||||
name: "Test with current system time",
|
||||
inputTrace: &Trace{
|
||||
startTime: time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got := test.inputTrace.TotalTime()
|
||||
if got == 0 {
|
||||
t.Errorf("Expected total time 0, got %d \n", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLog(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
msg string
|
||||
fields []Field
|
||||
expectedMessages []string
|
||||
sampleTrace *Trace
|
||||
}{
|
||||
{
|
||||
name: "Check the log dump with 3 msg",
|
||||
expectedMessages: []string{
|
||||
"msg1", "msg2", "msg3",
|
||||
},
|
||||
sampleTrace: &Trace{
|
||||
name: "Sample Trace",
|
||||
steps: []traceStep{
|
||||
{stepTime: time.Now(), msg: "msg1"},
|
||||
{stepTime: time.Now(), msg: "msg2"},
|
||||
{stepTime: time.Now(), msg: "msg3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Check formatting",
|
||||
expectedMessages: []string{
|
||||
"URL:/api,count:3", "msg1 str:text,int:2,bool:false", "msg2 x:1",
|
||||
},
|
||||
sampleTrace: &Trace{
|
||||
name: "Sample Trace",
|
||||
fields: []Field{{"URL", "/api"}, {"count", 3}},
|
||||
steps: []traceStep{
|
||||
{stepTime: time.Now(), msg: "msg1", fields: []Field{{"str", "text"}, {"int", 2}, {"bool", false}}},
|
||||
{stepTime: time.Now(), msg: "msg2", fields: []Field{{"x", "1"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Check fixture formatted",
|
||||
expectedMessages: []string{
|
||||
"URL:/api,count:3", "msg1 str:text,int:2,bool:false", "msg2 x:1",
|
||||
},
|
||||
sampleTrace: fieldsTraceFixture(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
klog.SetOutput(&buf)
|
||||
test.sampleTrace.Log()
|
||||
for _, msg := range test.expectedMessages {
|
||||
if !strings.Contains(buf.String(), msg) {
|
||||
t.Errorf("\nMsg %q not found in log: \n%v\n", msg, buf.String())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func fieldsTraceFixture() *Trace {
|
||||
trace := New("Sample Trace", Field{"URL", "/api"}, Field{"count", 3})
|
||||
trace.Step("msg1", Field{"str", "text"}, Field{"int", 2}, Field{"bool", false})
|
||||
trace.Step("msg2", Field{"x", "1"})
|
||||
return trace
|
||||
}
|
||||
|
||||
func TestLogIfLong(t *testing.T) {
|
||||
currentTime := time.Now()
|
||||
type mutate struct {
|
||||
delay time.Duration
|
||||
msg string
|
||||
}
|
||||
|
||||
tests := []*struct {
|
||||
name string
|
||||
expectedMessages []string
|
||||
sampleTrace *Trace
|
||||
threshold time.Duration
|
||||
mutateInfo []mutate // mutateInfo contains the information to mutate step's time to simulate multiple tests without waiting.
|
||||
|
||||
}{
|
||||
{
|
||||
name: "When threshold is 500 and msg 2 has highest share",
|
||||
expectedMessages: []string{
|
||||
"msg2",
|
||||
},
|
||||
mutateInfo: []mutate{
|
||||
{10, "msg1"},
|
||||
{1000, "msg2"},
|
||||
{0, "msg3"},
|
||||
},
|
||||
threshold: 500,
|
||||
},
|
||||
{
|
||||
name: "When threshold is 10 and msg 3 has highest share",
|
||||
expectedMessages: []string{
|
||||
"msg3",
|
||||
},
|
||||
mutateInfo: []mutate{
|
||||
{0, "msg1"},
|
||||
{0, "msg2"},
|
||||
{50, "msg3"},
|
||||
},
|
||||
threshold: 10,
|
||||
},
|
||||
{
|
||||
name: "When threshold is 0 and all msg have same share",
|
||||
expectedMessages: []string{
|
||||
"msg1", "msg2", "msg3",
|
||||
},
|
||||
mutateInfo: []mutate{
|
||||
{0, "msg1"},
|
||||
{0, "msg2"},
|
||||
{0, "msg3"},
|
||||
},
|
||||
threshold: 0,
|
||||
},
|
||||
{
|
||||
name: "When threshold is 20 and all msg 1 has highest share",
|
||||
expectedMessages: []string{},
|
||||
mutateInfo: []mutate{
|
||||
{10, "msg1"},
|
||||
{0, "msg2"},
|
||||
{0, "msg3"},
|
||||
},
|
||||
threshold: 20,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
klog.SetOutput(&buf)
|
||||
|
||||
tt.sampleTrace = New("Test trace")
|
||||
|
||||
for index, mod := range tt.mutateInfo {
|
||||
tt.sampleTrace.Step(mod.msg)
|
||||
tt.sampleTrace.steps[index].stepTime = currentTime.Add(mod.delay)
|
||||
}
|
||||
|
||||
tt.sampleTrace.LogIfLong(tt.threshold)
|
||||
|
||||
for _, msg := range tt.expectedMessages {
|
||||
if msg != "" && !strings.Contains(buf.String(), msg) {
|
||||
t.Errorf("Msg %q expected in trace log: \n%v\n", msg, buf.String())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleTrace_Step() {
|
||||
t := New("frobber")
|
||||
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
t.Step("reticulated splines") // took 5ms
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
t.Step("sequenced particles") // took 10ms
|
||||
|
||||
klog.SetOutput(os.Stdout) // change output from stderr to stdout
|
||||
t.Log()
|
||||
}
|
||||
Reference in New Issue
Block a user