merging refactor to master
commit
15667435be
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"GOLANG": {
|
||||
"TOO_MANY_IVARS": [
|
||||
10,
|
||||
12,
|
||||
15,
|
||||
18
|
||||
],
|
||||
"LOC": [
|
||||
30,
|
||||
50,
|
||||
80,
|
||||
100
|
||||
],
|
||||
"TOTAL_LOC": [
|
||||
200,
|
||||
400,
|
||||
500,
|
||||
600
|
||||
],
|
||||
"TOO_MANY_FUNCTIONS": [
|
||||
30,
|
||||
40,
|
||||
45,
|
||||
46
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
# This file contains all available configuration options
|
||||
# with their default values.
|
||||
|
||||
# options for analysis running
|
||||
run:
|
||||
# default concurrency is a available CPU number
|
||||
concurrency: 4
|
||||
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
timeout: 1m
|
||||
|
||||
# exit code when at least one issue was found, default is 1
|
||||
issues-exit-code: 1
|
||||
|
||||
# include test files or not, default is true
|
||||
tests: true
|
||||
|
||||
# list of build tags, all linters use it. Default is empty list.
|
||||
# build-tags:
|
||||
# - mytag
|
||||
|
||||
# which dirs to skip: issues from them won't be reported;
|
||||
# can use regexp here: generated.*, regexp is applied on full path;
|
||||
# default value is empty list, but default dirs are skipped independently
|
||||
# from this option's value (see skip-dirs-use-default).
|
||||
# skip-dirs:
|
||||
# - src/external_libs
|
||||
# - autogenerated_by_my_lib
|
||||
|
||||
# default is true. Enables skipping of directories:
|
||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||
skip-dirs-use-default: true
|
||||
|
||||
# which files to skip: they will be analyzed, but issues from them
|
||||
# won't be reported. Default value is empty list, but there is
|
||||
# no need to include all autogenerated files, we confidently recognize
|
||||
# autogenerated files. If it's not please let us know.
|
||||
# skip-files:
|
||||
# - ".*\\.my\\.go$"
|
||||
# - lib/bad.go
|
||||
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
|
||||
# If invoked with -mod=readonly, the go command is disallowed from the implicit
|
||||
# automatic updating of go.mod described above. Instead, it fails when any changes
|
||||
# to go.mod are needed. This setting is most useful to check that go.mod does
|
||||
# not need updates, such as in a continuous integration and testing system.
|
||||
# If invoked with -mod=vendor, the go command assumes that the vendor
|
||||
# directory holds the correct copies of dependencies and ignores
|
||||
# the dependency descriptions in go.mod.
|
||||
# modules-download-mode: readonly|release|vendor
|
||||
|
||||
# output configuration options
|
||||
output:
|
||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
||||
format: colored-line-number
|
||||
|
||||
# print lines of code with issue, default is true
|
||||
print-issued-lines: true
|
||||
|
||||
# print linter name in the end of issue text, default is true
|
||||
print-linter-name: true
|
||||
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
errcheck:
|
||||
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-type-assertions: true
|
||||
|
||||
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-blank: false
|
||||
|
||||
# [deprecated] comma-separated list of pairs of the form pkg:regex
|
||||
# the regex is used to ignore names within pkg. (default "fmt:.*").
|
||||
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
|
||||
# ignore: fmt:.*,io/ioutil:^Read.*
|
||||
# path to a file containing a list of functions to exclude from checking
|
||||
# see https://github.com/kisielk/errcheck#excluding-functions for details
|
||||
# exclude: /path/to/file.txt
|
||||
|
||||
funlen:
|
||||
lines: 60
|
||||
statements: 40
|
||||
|
||||
govet:
|
||||
# report about shadowed variables
|
||||
check-shadowing: true
|
||||
|
||||
# settings per analyzer
|
||||
settings:
|
||||
printf: # analyzer name, run `go tool vet help` to see all analyzers
|
||||
funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
|
||||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
|
||||
|
||||
# enable or disable analyzers by name
|
||||
enable:
|
||||
- atomicalign
|
||||
enable-all: false
|
||||
disable:
|
||||
# - shadow
|
||||
disable-all: false
|
||||
golint:
|
||||
# minimal confidence for issues, default is 0.8
|
||||
min-confidence: 0.8
|
||||
gofmt:
|
||||
# simplify code: gofmt with `-s` option, true by default
|
||||
simplify: true
|
||||
goimports:
|
||||
# put imports beginning with prefix after 3rd-party packages;
|
||||
# it's a comma-separated list of prefixes
|
||||
local-prefixes: github.com/org/project
|
||||
gocyclo:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 10
|
||||
gocognit:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 20
|
||||
maligned:
|
||||
# print struct with more effective memory layout or not, false by default
|
||||
suggest-new: true
|
||||
dupl:
|
||||
# tokens count to trigger issue, 150 by default
|
||||
threshold: 100
|
||||
goconst:
|
||||
# minimal length of string constant, 3 by default
|
||||
min-len: 3
|
||||
# minimal occurrences count to trigger, 3 by default
|
||||
min-occurrences: 3
|
||||
depguard:
|
||||
list-type: blacklist
|
||||
include-go-root: false
|
||||
packages:
|
||||
- github.com/sirupsen/logrus
|
||||
packages-with-error-messages:
|
||||
# specify an error message to output when a blacklisted package is used
|
||||
github.com/sirupsen/logrus: "logging is allowed only by logutils.Log"
|
||||
misspell:
|
||||
# Correct spellings using locale preferences for US or UK.
|
||||
# Default is to use a neutral variety of English.
|
||||
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
|
||||
locale: US
|
||||
ignore-words:
|
||||
- someword
|
||||
lll:
|
||||
# max line length, lines longer will be reported. Default is 120.
|
||||
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
|
||||
line-length: 120
|
||||
# tab width in spaces. Default to 1.
|
||||
tab-width: 1
|
||||
unused:
|
||||
# treat code as a program (not a library) and report unused exported identifiers; default is false.
|
||||
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
unparam:
|
||||
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
|
||||
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
nakedret:
|
||||
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
|
||||
max-func-lines: 30
|
||||
prealloc:
|
||||
# XXX: we don't recommend using this linter before doing performance profiling.
|
||||
# For most programs usage of prealloc will be a premature optimization.
|
||||
|
||||
# Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.
|
||||
# True by default.
|
||||
simple: true
|
||||
range-loops: true # Report preallocation suggestions on range loops, true by default
|
||||
for-loops: false # Report preallocation suggestions on for loops, false by default
|
||||
gocritic:
|
||||
# Which checks should be enabled; can't be combined with 'disabled-checks';
|
||||
# See https://go-critic.github.io/overview#checks-overview
|
||||
# To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run`
|
||||
# By default list of stable checks is used.
|
||||
# enabled-checks:
|
||||
# - rangeValCopy
|
||||
|
||||
# Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty
|
||||
disabled-checks:
|
||||
- regexpMust
|
||||
|
||||
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
|
||||
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
|
||||
enabled-tags:
|
||||
- performance
|
||||
|
||||
settings: # settings passed to gocritic
|
||||
captLocal: # must be valid enabled check name
|
||||
paramsOnly: true
|
||||
rangeValCopy:
|
||||
sizeThreshold: 32
|
||||
godox:
|
||||
# report any comments starting with keywords, this is useful for TODO or FIXME comments that
|
||||
# might be left in the code accidentally and should be resolved before merging
|
||||
keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting
|
||||
- NOTE
|
||||
- OPTIMIZE # marks code that should be optimized before merging
|
||||
- HACK # marks hack-arounds that should be removed before merging
|
||||
dogsled:
|
||||
# checks assignments with too many blank identifiers; default is 2
|
||||
max-blank-identifiers: 2
|
||||
|
||||
whitespace:
|
||||
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
|
||||
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
|
||||
wsl:
|
||||
# If true append is only allowed to be cuddled if appending value is
|
||||
# matching variables, fields or types on line above. Default is true.
|
||||
strict-append: true
|
||||
# Allow calls and assignments to be cuddled as long as the lines have any
|
||||
# matching variables, fields or types. Default is true.
|
||||
allow-assign-and-call: true
|
||||
# Allow multiline assignments to be cuddled. Default is true.
|
||||
allow-multiline-assign: true
|
||||
# Allow declarations (var) to be cuddled.
|
||||
allow-cuddle-declarations: false
|
||||
# Allow trailing comments in ending of blocks
|
||||
allow-trailing-comment: false
|
||||
# Force newlines in end of case at this limit (0 = never).
|
||||
force-case-trailing-whitespace: 0
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- megacheck
|
||||
- govet
|
||||
- funlen
|
||||
- gocyclo
|
||||
disable:
|
||||
- maligned
|
||||
- prealloc
|
||||
- gosec
|
||||
disable-all: false
|
||||
presets:
|
||||
- bugs
|
||||
- unused
|
||||
fast: false
|
||||
|
||||
issues:
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
# But independently from this option we use default exclude patterns,
|
||||
# it can be disabled by `exclude-use-default: false`. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`
|
||||
exclude:
|
||||
- abcdef
|
||||
|
||||
# Excluding configuration per-path, per-linter, per-text and per-source
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on tests files.
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gocyclo
|
||||
- errcheck
|
||||
- dupl
|
||||
- gosec
|
||||
- funlen
|
||||
- goconst
|
||||
- gocognit
|
||||
|
||||
# Exclude known linters from partially hard-vendored code,
|
||||
# which is impossible to exclude via "nolint" comments.
|
||||
- path: internal/hmac/
|
||||
text: "weak cryptographic primitive"
|
||||
linters:
|
||||
- gosec
|
||||
|
||||
# Exclude some staticcheck messages
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "SA9003:"
|
||||
|
||||
# Exclude lll issues for long lines with go:generate
|
||||
- linters:
|
||||
- lll
|
||||
source: "^//go:generate "
|
||||
|
||||
# Independently from option `exclude` we use default exclude patterns,
|
||||
# it can be disabled by this option. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`.
|
||||
# Default value for this option is true.
|
||||
exclude-use-default: false
|
||||
|
||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||
max-issues-per-linter: 0
|
||||
|
||||
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
||||
max-same-issues: 0
|
||||
|
||||
# Show only new issues: if there are unstaged changes or untracked files,
|
||||
# only those changes are analyzed, else only changes in HEAD~ are analyzed.
|
||||
# It's a super-useful option for integration of golangci-lint into existing
|
||||
# large codebase. It's not practical to fix all existing issues at the moment
|
||||
# of integration: much better don't allow issues in new code.
|
||||
# Default is false.
|
||||
new: false
|
||||
# Show only new issues created after git revision `REV`
|
||||
# new-from-rev: REV
|
||||
# Show only new issues created in git patch with set file path.
|
||||
# new-from-patch: path/to/patch/file
|
||||
69
README.md
69
README.md
|
|
@ -75,7 +75,6 @@ K9s is available on Linux, OSX and Windows platforms.
|
|||
1. Deployments
|
||||
<img src="assets/screen_dp.png"/>
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Demo Video
|
||||
|
|
@ -112,7 +111,7 @@ K9s uses aliases to navigate most K8s resources.
|
|||
| `Ctrl-a` | Show all available resource alias | select+`<ENTER>` to view |
|
||||
| `/`filter`ENTER` | Filter out a resource view given a filter | `/bumblebeetuna` |
|
||||
| `/`-l label-selector`ENTER` | Filter resource view by labels | `/-l app=fred` |
|
||||
| `<Esc>` | Bails out of command/filter mode | |
|
||||
| `<Esc>` | Bails out of view/command/filter mode | |
|
||||
| `d`,`v`, `e`, `l`,... | Key mapping to describe, view, edit, view logs,... | `d` (describes a resource) |
|
||||
| `:`ctx`<ENTER>` | To view and switch to another Kubernetes context | `:`+`ctx`+`<ENTER>` |
|
||||
| `:`ns`<ENTER>` | To view and switch to another Kubernetes namespace | `:`+`ns`+`<ENTER>` |
|
||||
|
|
@ -124,11 +123,12 @@ K9s uses aliases to navigate most K8s resources.
|
|||
|
||||
## K9s config file ($HOME/.k9s/config.yml)
|
||||
|
||||
K9s keeps its configurations in a dot file in your home directory.
|
||||
K9s keeps its configurations in a .k9s directory in your home directory.
|
||||
|
||||
> NOTE: This is still in flux and will change while in pre-release stage!
|
||||
|
||||
```yaml
|
||||
# config.yml
|
||||
k9s:
|
||||
# Indicates api-server poll intervals.
|
||||
refreshRate: 2
|
||||
|
|
@ -162,9 +162,10 @@ K9s uses aliases to navigate most K8s resources.
|
|||
```
|
||||
|
||||
---
|
||||
|
||||
## Aliases
|
||||
|
||||
In K9s you can define your own command aliases (shortnames) to access your resources. In your `$HOME/.k9s` define a file called `alias.yml`. A K9s alias defines pairs of alias:gvr. A gvr represents a fully qualified Kubernetes resource identifier. Here is an example of an alias file:
|
||||
In K9s, you can define your own command aliases (shortnames) to access your resources. In your `$HOME/.k9s` define a file called `alias.yml`. A K9s alias defines pairs of alias:gvr. A gvr represents a fully qualified Kubernetes resource identifier. Here is an example of an alias file:
|
||||
|
||||
```yaml
|
||||
# $HOME/.k9s/alias.yml
|
||||
|
|
@ -173,9 +174,10 @@ alias:
|
|||
crb: rbac.authorization.k8s.io/v1/clusterrolebindings
|
||||
```
|
||||
|
||||
Using this alias file, you can now type pp/crb to list pods, clusterrolebindings respectively.
|
||||
Using this alias file, you can now type pp/crb to list pods or clusterrolebindings respectively.
|
||||
|
||||
---
|
||||
|
||||
## Plugins
|
||||
|
||||
K9s allows you to define your own cluster commands via plugins. K9s will look at `$HOME/.k9s/plugin.yml` to locate available plugins. A plugin is defined as follows:
|
||||
|
|
@ -183,9 +185,10 @@ K9s allows you to define your own cluster commands via plugins. K9s will look at
|
|||
```yaml
|
||||
# $HOME/.k9s/plugin.yml
|
||||
plugin:
|
||||
# Defines a plugin to provide a `Ctrl-L` shorcut to tail the logs while in pod view.
|
||||
fred:
|
||||
shortCut: Ctrl-L
|
||||
description: "Pod logs"
|
||||
description: Pod logs
|
||||
scopes:
|
||||
- po
|
||||
command: /usr/local/bin/kubectl
|
||||
|
|
@ -202,7 +205,7 @@ plugin:
|
|||
|
||||
This defines a plugin for viewing logs on a selected pod using `CtrlL` mnemonic.
|
||||
|
||||
The shortcut option represents the command a user would type to activate the plugin. The command represents adhoc commands the plugin runs upon activation. The scopes defines a collection of views shortnames for which the plugin shortcut will be made available to the user.
|
||||
The shortcut option represents the command a user would type to activate the plugin. The command represents adhoc commands the plugin runs upon activation. The scopes defines a collection of resources names/shortnames for which the plugin shortcut will be made available to the user. You can specify all to provide this shortcut for all views.
|
||||
|
||||
K9s does provide additional environment variables for you to customize your plugins. Currently, the available environment variables are as follows:
|
||||
|
||||
|
|
@ -283,6 +286,41 @@ benchmarks:
|
|||
|
||||
---
|
||||
|
||||
## HotKeys
|
||||
|
||||
Entering the command mode and typing a resource name or alias, could be cumbersome for navigating thru often used resources. We're introducing hotkeys that allows a user to define their own hotkeys to activate their favorite resource views. In order to enable hotkeys please follow these steps:
|
||||
|
||||
1. In your .k9s home directory create a file named `hotkey.yml`
|
||||
2. Add the following to your `hotkey.yml`. You can use short names or resource name to specify a command ie same as typing it in command mode.
|
||||
|
||||
```yaml
|
||||
hotKey:
|
||||
shift-0:
|
||||
shortCut: Shift-0
|
||||
description: View pods
|
||||
command: pods
|
||||
shift-1:
|
||||
shortCut: Shift-1
|
||||
description: View deployments
|
||||
command: dp
|
||||
shift-2:
|
||||
shortCut: Shift-2
|
||||
description: View services
|
||||
command: service
|
||||
shift-3:
|
||||
shortCut: Shift-3
|
||||
description: View statefulsets
|
||||
command: sts
|
||||
```
|
||||
|
||||
Not feeling so hot? Your custom hotkeys list will be listed in the help view.`<?>`. Also your hotkey file will be automatically reloaded so you can readily use your hotkeys as you define them.
|
||||
|
||||
You can choose any keyboard shotcuts that make sense to you, provided they are not part of the standard K9s shortcuts list.
|
||||
|
||||
NOTE: This feature/configuration might change in future releases!
|
||||
|
||||
---
|
||||
|
||||
## K9s RBAC FU
|
||||
|
||||
On RBAC enabled clusters, you would need to give your users/groups capabilities so that they can use K9s to explore their Kubernetes cluster. K9s needs minimally read privileges at both the cluster and namespace level to display resources and metrics.
|
||||
|
|
@ -290,7 +328,6 @@ On RBAC enabled clusters, you would need to give your users/groups capabilities
|
|||
These rules below are just suggestions. You will need to customize them based on your environment policies. If you need to edit/delete resources extra Fu will be necessary.
|
||||
|
||||
> NOTE! Cluster/Namespace access may change in the future as K9s evolves.
|
||||
|
||||
> NOTE! We expect K9s to keep running even in atrophied clusters/namespaces. Please file issues if this is not the case!
|
||||
|
||||
### Cluster RBAC scope
|
||||
|
|
@ -315,7 +352,7 @@ rules:
|
|||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
# Grants RO access to metric server
|
||||
# Grants RO access to metric server (if present)
|
||||
- apiGroups: ["metrics.k8s.io"]
|
||||
resources: ["nodes", "pods"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
|
|
@ -355,7 +392,7 @@ rules:
|
|||
verbs: ["get", "list", "watch"]
|
||||
# Grants RO access to metric server
|
||||
- apiGroups: ["metrics.k8s.io"]
|
||||
resources: ["pods"]
|
||||
resources: ["pods", "nodes"]
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
|
|
@ -382,7 +419,7 @@ roleRef:
|
|||
|
||||
## Skins
|
||||
|
||||
You can style K9s based on your own sense of style and look. This is very much an experimental feature at this time, more will be added/modified if this feature has legs so thread accordingly!
|
||||
You can style K9s based on your own sense of look and style. This is very much an experimental feature at this time, more will be added/modified if this feature has legs so thread accordingly!
|
||||
|
||||
By default a K9s view displays resource information using the following coloring scheme:
|
||||
|
||||
|
|
@ -392,7 +429,8 @@ By default a K9s view displays resource information using the following coloring
|
|||
|
||||
Skins are YAML files, that enable a user to change K9s presentation layer. K9s skins are loaded from `$HOME/.k9s/skin.yml`. If a skin file is detected then the skin would be loaded if not the current stock skin remains in effect.
|
||||
|
||||
Below is a sample skin file, more skins would be available in the skins directory, just simply copy any of these in your user's home dir as `skin.yml`.
|
||||
You can also change K9s skins based on the cluster you are connecting too. In this case, you can specify the skin file name as `$HOME/.k9s/mycluster_skin.yml`
|
||||
Below is a sample skin file, more skins are available in the skins directory in this repo, just simply copy any of these in your user's home dir as `skin.yml`.
|
||||
|
||||
```yaml
|
||||
# InTheNavy Skin...
|
||||
|
|
@ -424,7 +462,8 @@ k9s:
|
|||
activeColor: skyblue
|
||||
# Resource status and update styles
|
||||
status:
|
||||
newColor: blue
|
||||
# You can also use hex colors!
|
||||
newColor: #0000ff
|
||||
modifyColor: powderblue
|
||||
addColor: lightskyblue
|
||||
errorColor: indianred
|
||||
|
|
@ -501,7 +540,7 @@ Available color names are defined below:
|
|||
|
||||
This initial drop is brittle. K9s will most likely blow up...
|
||||
|
||||
1. You're running older versions of Kubernetes. K9s works best Kubernetes 1.12+.
|
||||
1. You're running older versions of Kubernetes. K9s works best Kubernetes 1.15+.
|
||||
2. You don't have enough RBAC fu to manage your cluster.
|
||||
|
||||
---
|
||||
|
|
@ -516,7 +555,7 @@ dig this effort, please let us know that too!
|
|||
|
||||
## ATTA Girls/Boys!
|
||||
|
||||
K9s sits on top of many of opensource projects and libraries. Our *sincere*
|
||||
K9s sits on top of many open source projects and libraries. Our *sincere*
|
||||
appreciations to all the OSS contributors that work nights and weekends
|
||||
to make this project a reality!
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
|
||||
|
||||
# Release v0.10.0
|
||||
|
||||
## Notes
|
||||
|
||||
Thank you to all that contributed with flushing out issues and enhancements for K9s! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make K9s better is as ever very much noticed and appreciated!
|
||||
|
||||
Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
|
||||
|
||||
---
|
||||
|
||||
## Change Logs
|
||||
|
||||
First off, Happy 2020 to you and yours!! Best wishes for good health and good fortune!
|
||||
|
||||
This release represents a major overall of K9s core. It's been a long time coming and indeed a long day in the saddle ;( There has been many code changes and hopefully improvements from previous releases. I think some of it is better but I've probably borked a bunch of functionality in the process. I look to you to help me flesh out issues and bugs, so we can move on to bigger and exciting features in 2020! Please do thread lightly on this one and make sure to keep a previous release handy just in case... This was a boatload of work to make this happen, my hope is you'll enjoy some of the improvements... In any case, and as always, if you feel they're better ways or imperfections by all means please pipe in!
|
||||
|
||||
I would also like to take this opportunity to thank all of you for your kind PRs and issues and for your support and patience with K9s. I understand this release might be a bit torked, but I will work hard to make sure we reach stability quickly in the next few drops. Thank you for your understanding!!
|
||||
|
||||
## VatDoesDisDo?
|
||||
|
||||
Most of the refactors are around K8s resource fetching and viewing as well as navigation changes. Based on our observations this release might load resources a bit slower than usual but should make navigation much faster once the cache is primed. We've made some improvements to be more consistent with navigation and shortcuts management. We've got ride off the breadcrumbs navigation ie no more `p` to nav back. Crumbs are now just tracking a natural resoure navigation ie pod -> containers -> logs and no longer commands history. Each new command will now load a brand new breadcrumb. You can press `<esc>` to nav back to the previous page. We're also introducing a new hotkeys feature, that afforts creating shortcuts to navigate to your favorite resources ie shift-0 -> view pods, shift-1 -> view deployments (See HotKey section below). I know there were many outstanding PRS (Thank you to all that I've submitted!) and given the extent of the changes, I've resolved to incorporate them in manually vs having to deal with merge conflicts.
|
||||
|
||||
## Custom Skins Per Cluster
|
||||
|
||||
In this release, we've added support for skins at the cluster level. Do you want K9s to look differently based on which cluster you're connecting to? All you'll need is to name the skin file in the K9s home directory as follows `mycluster_skin.yml`. If no cluster specific skin file is found, the standard `skin.yml` file will be loaded if present. Please checkout the `skins` directory in this repo or PR me if you have cool skins you'd like to share with your fellow K9ers as they will be featured in these release notes and the project README.
|
||||
|
||||
## Hot(Ness)?
|
||||
|
||||
Feeling like you want to be able to quickly switch around your favorite resources with your very own shortcut? Wouldn't it be dandy to navigate to your deployments using shift-0 vs entering a command `:dp`? Here is what you'll need to do to add HotKeys to your K9s sessions:
|
||||
|
||||
1. In your .k9s home directory create a file named `hotkey.yml`
|
||||
2. For example add the following to your `hotkey.yml`. You can use short names or resource name to specify a command ie same as typing it in command mode.
|
||||
|
||||
```yaml
|
||||
hotKey:
|
||||
shift-0:
|
||||
shortCut: Shift-0
|
||||
description: View pods
|
||||
command: pods
|
||||
shift-1:
|
||||
shortCut: Shift-1
|
||||
description: View deployments
|
||||
command: dp
|
||||
shift-2:
|
||||
shortCut: Shift-2
|
||||
description: View services
|
||||
command: service
|
||||
shift-3:
|
||||
shortCut: Shift-3
|
||||
description: View statefulsets
|
||||
command: statefulsets
|
||||
```
|
||||
|
||||
Not feeling so hot? Your custom hotkeys list will be listed in the help view.`<?>`.
|
||||
|
||||
You can choose any keyboard shotcuts that make sense to you, provided they are not part of the standard K9s shortcuts list.
|
||||
|
||||
## PullRequests
|
||||
|
||||
* [PR #447](https://github.com/derailed/k9s/pull/447) K9s MacPorts support. Thank you! [Nils Breunese](https://github.com/breun)
|
||||
* [PR #446](https://github.com/derailed/k9s/pull/446) Same key invert sort. Big thanks!! [James Hiew](https://github.com/jameshiew)
|
||||
* [PR #445](https://github.com/derailed/k9s/pull/445) Use `?` to toggle help. Major thanks!! [Ramz](https://github.com/ageekymonk)
|
||||
* [PR #443](https://github.com/derailed/k9s/pull/443) Hex color skin support. Great work! [Gavin Ray](https://github.com/gavinray97)
|
||||
* [PR #442](https://github.com/derailed/k9s/pull/442) Full screen/Wrap support on log view. ATTA BOY! [Shiv3](https://github.com/shiv3)
|
||||
* [PR #412](https://github.com/derailed/k9s/pull/412) Simplify cruder interface. ATTA BOY!! (as always)[Gustavo Silva Paiva](https://github.com/paivagustavo)
|
||||
* [PR #350](https://github.com/derailed/k9s/pull/350) Sanitize file name before saving. All credits to [Tuomo Syvänperä](https://github.com/syvanpera)
|
||||
|
||||
---
|
||||
|
||||
## Resolved Bugs/Features
|
||||
|
||||
* [Issue #437](https://github.com/derailed/k9s/issues/437) Error when viewing cluster role on a role binding.
|
||||
* [Issue #434](https://github.com/derailed/k9s/issues/434) Same key `?` toggle help.
|
||||
* [Issue #432](https://github.com/derailed/k9s/issues/432) Add address field to port forwards.
|
||||
* [Issue #431](https://github.com/derailed/k9s/issues/431) Add LimitRange resource support.
|
||||
* [Issue #430](https://github.com/derailed/k9s/issues/430) Add HotKey support.
|
||||
* [Issue #426](https://github.com/derailed/k9s/issues/426) Address slow scroll while in table view.
|
||||
* [Issue #417](https://github.com/derailed/k9s/issues/417) Ensure code lints correctly. Thank you Gustavo!!
|
||||
* [Issue #415](https://github.com/derailed/k9s/issues/415) Add provisions to support longer clusterinfo/namespace header.
|
||||
* [Issue #408](https://github.com/derailed/k9s/issues/408) Same key toggle inverse sort.
|
||||
* [Issue #402](https://github.com/derailed/k9s/issues/402) Add `all` support to plugin scope.
|
||||
* [Issue #401](https://github.com/derailed/k9s/issues/401) Add support for custom plugins on all views.
|
||||
|
||||
---
|
||||
|
||||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
|
@ -26,7 +26,7 @@ Also if you dig this tool, please make some noise on social! [@kitesurfer](https
|
|||
|
||||
### o YAML Highlighter
|
||||
|
||||
Describe and YAML commands will now yield syntax highlighted views.
|
||||
Describe and YAML commands will now yield syntax highlighted view.
|
||||
[Feature #142](https://github.com/derailed/k9s/issues/142)
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ This is a maintenance release to mainly resolve outstanding issues and bugs.
|
|||
|
||||
### Tracking Percentages
|
||||
|
||||
Added two new columns to track cpu/memory utilization on metrics-server enabled clusters. These apply to pod,container and node views.
|
||||
|
||||
Added two new columns to track cpu/memory utilization on metrics-server enabled clusters. These apply to pod,container and node view.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ This feature is still work in progress. It does require a new config file to hel
|
|||
|
||||
This is one feature that I think is, pardon my french.., totally `Bitch'n`! K9s now bundles [Hey](https://github.com/rakyll/hey) CLI tool from the extremely talented Jaana Dogan of Google fame. Hey allows you to benchmark HTTP service endpoints similar to apache bench.
|
||||
|
||||
Benchmarking is enabled via keyboard shortcuts `Ctrl-B` and `Alt-B` to activate/cancel a benchmark while in PortForward and Service views. Benchmarking a service assumes the HTTP service is exposed either as NodePort or LoadBalancer. To view your benchmarks, navigate to the new Benchmark view aka `:be<ENTER>` to list your benchmarks and runs statistics.
|
||||
Benchmarking is enabled via keyboard shortcuts `Ctrl-B` and `Alt-B` to activate/cancel a benchmark while in PortForward and Service view. Benchmarking a service assumes the HTTP service is exposed either as NodePort or LoadBalancer. To view your benchmarks, navigate to the new Benchmark view aka `:be<ENTER>` to list your benchmarks and runs statistics.
|
||||
|
||||
So you now have the ability to stretch out your cluster legs by benchmarking your pods and services and gather all kind of interesting statistics directly in K9s. Generating load on your resources will help you tune your cluster resources, exercise your auto scalers, compare Canary builds perf, etc...
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ dialogs. This was totally a reasonable thing to do! However in case of managed p
|
|||
|
||||
This one is cool! I think this thought came about from (Markus)[https://github.com/Makusi75]. Thank you Markus!! This feature allows K9s users to now customize K9s with their own plugin commands. You will be able to add a new menu shortcut to the K9s menu and fire off a custom command on a selected resource. Some of you might be leveraging kubectl plugins and now you will be able to fire these off directly from K9s along with many other shell commands.
|
||||
|
||||
In order to specify a custom plugin command, you will need to modify your .k9s/config.yml file. For example here is a sample extension to list out all the pods in the `fred` namespace while in a pod or deployment views. When this plugin is available a new command `<alt-p>` will show only while in pod and deploy views.
|
||||
In order to specify a custom plugin command, you will need to modify your .k9s/config.yml file. For example here is a sample extension to list out all the pods in the `fred` namespace while in a pod or deployment view. When this plugin is available a new command `<alt-p>` will show only while in pod and deploy view.
|
||||
|
||||
```yaml
|
||||
plugins:
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Also if you dig this tool, please make some noise on social! [@kitesurfer](https
|
|||
|
||||
### NetworkPolicy
|
||||
|
||||
NetworkPolicy resource is now available under the command `np` while in command mode. It behaves like other K9s resource views. You will get a bit more information in K9s vs `kubectl` as it includes information about ingress and egress rules.
|
||||
NetworkPolicy resource is now available under the command `np` while in command mode. It behaves like other K9s resource view. You will get a bit more information in K9s vs `kubectl` as it includes information about ingress and egress rules.
|
||||
|
||||
### Arrrggg! New CLI Argument
|
||||
|
||||
|
|
|
|||
52
cmd/root.go
52
cmd/root.go
|
|
@ -5,11 +5,11 @@ import (
|
|||
"fmt"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/color"
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/derailed/k9s/internal/views"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/view"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
|
|
@ -34,20 +34,29 @@ var (
|
|||
Long: longAppDesc,
|
||||
Run: run,
|
||||
}
|
||||
_ config.KubeSettings = &k8s.Config{}
|
||||
_ config.KubeSettings = &client.Config{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
const falseFlag = "false"
|
||||
rootCmd.AddCommand(versionCmd(), infoCmd())
|
||||
initK9sFlags()
|
||||
initK8sFlags()
|
||||
|
||||
// Klogs (of course) want to print stuff to the screen ;(
|
||||
klog.InitFlags(nil)
|
||||
flag.Set("log_file", config.K9sLogs)
|
||||
flag.Set("stderrthreshold", "fatal")
|
||||
flag.Set("alsologtostderr", "false")
|
||||
flag.Set("logtostderr", "false")
|
||||
if err := flag.Set("log_file", config.K9sLogs); err != nil {
|
||||
log.Error().Err(err)
|
||||
}
|
||||
if err := flag.Set("stderrthreshold", "fatal"); err != nil {
|
||||
log.Error().Err(err)
|
||||
}
|
||||
if err := flag.Set("alsologtostderr", falseFlag); err != nil {
|
||||
log.Error().Err(err)
|
||||
}
|
||||
if err := flag.Set("logtostderr", falseFlag); err != nil {
|
||||
log.Error().Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Execute root command
|
||||
|
|
@ -59,22 +68,24 @@ func Execute() {
|
|||
|
||||
func run(cmd *cobra.Command, args []string) {
|
||||
defer func() {
|
||||
// views.ClearScreen()
|
||||
// view.ClearScreen()
|
||||
if err := recover(); err != nil {
|
||||
log.Error().Msgf("Boom! %v", err)
|
||||
log.Error().Msg(string(debug.Stack()))
|
||||
printLogo(color.Red)
|
||||
fmt.Printf(color.Colorize("Boom!! ", color.Red))
|
||||
fmt.Printf("%s", color.Colorize("Boom!! ", color.Red))
|
||||
fmt.Println(color.Colorize(fmt.Sprintf("%v.", err), color.White))
|
||||
}
|
||||
}()
|
||||
|
||||
zerolog.SetGlobalLevel(parseLevel(*k9sFlags.LogLevel))
|
||||
cfg := loadConfiguration()
|
||||
app := views.NewApp(cfg)
|
||||
app := view.NewApp(cfg)
|
||||
{
|
||||
defer app.BailOut()
|
||||
app.Init(version, *k9sFlags.RefreshRate)
|
||||
if err := app.Init(version, *k9sFlags.RefreshRate); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
app.Run()
|
||||
}
|
||||
}
|
||||
|
|
@ -83,8 +94,9 @@ func loadConfiguration() *config.Config {
|
|||
log.Info().Msg("🐶 K9s starting up...")
|
||||
|
||||
// Load K9s config file...
|
||||
k8sCfg := k8s.NewConfig(k8sFlags)
|
||||
k8sCfg := client.NewConfig(k8sFlags)
|
||||
k9sCfg := config.NewConfig(k8sCfg)
|
||||
|
||||
if err := k9sCfg.Load(config.K9sConfigFile); err != nil {
|
||||
log.Warn().Msg("Unable to locate K9s config. Generating new configuration...")
|
||||
}
|
||||
|
|
@ -101,25 +113,31 @@ func loadConfiguration() *config.Config {
|
|||
k9sCfg.K9s.OverrideCommand(*k9sFlags.Command)
|
||||
}
|
||||
|
||||
if k9sFlags.AllNamespaces != nil && *k9sFlags.AllNamespaces {
|
||||
k9sCfg.SetActiveNamespace(resource.AllNamespaces)
|
||||
if isBoolSet(k9sFlags.AllNamespaces) && k9sCfg.SetActiveNamespace(render.AllNamespaces) != nil {
|
||||
log.Error().Msg("Setting active namespace")
|
||||
}
|
||||
|
||||
if err := k9sCfg.Refine(k8sFlags); err != nil {
|
||||
log.Panic().Err(err).Msg("Unable to locate kubeconfig file")
|
||||
}
|
||||
k9sCfg.SetConnection(k8s.InitConnectionOrDie(k8sCfg, log.Logger))
|
||||
k9sCfg.SetConnection(client.InitConnectionOrDie(k8sCfg))
|
||||
|
||||
// Try to access server version if that fail. Connectivity issue?
|
||||
if _, err := k9sCfg.GetConnection().ServerVersion(); err != nil {
|
||||
log.Panic().Err(err).Msg("K9s can't connect to cluster")
|
||||
}
|
||||
log.Info().Msg("✅ Kubernetes connectivity")
|
||||
k9sCfg.Save()
|
||||
if err := k9sCfg.Save(); err != nil {
|
||||
log.Error().Err(err).Msg("Config save")
|
||||
}
|
||||
|
||||
return k9sCfg
|
||||
}
|
||||
|
||||
func isBoolSet(b *bool) bool {
|
||||
return b != nil && *b
|
||||
}
|
||||
|
||||
func parseLevel(level string) zerolog.Level {
|
||||
switch level {
|
||||
case "debug":
|
||||
|
|
|
|||
12
go.mod
12
go.mod
|
|
@ -28,14 +28,15 @@ replace (
|
|||
|
||||
require (
|
||||
github.com/atotto/clipboard v0.1.2
|
||||
github.com/derailed/tview v0.3.2
|
||||
github.com/derailed/tview v0.3.3
|
||||
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
|
||||
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/gdamore/tcell v1.3.0
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect
|
||||
github.com/golang/mock v1.2.0
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
github.com/googleapis/gnostic v0.2.0 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc // indirect
|
||||
|
|
@ -44,22 +45,23 @@ require (
|
|||
github.com/mattn/go-runewidth v0.0.5
|
||||
github.com/petergtz/pegomock v2.6.0+incompatible
|
||||
github.com/rakyll/hey v0.1.2
|
||||
github.com/rs/zerolog v1.14.3
|
||||
github.com/rs/zerolog v1.17.2
|
||||
github.com/sahilm/fuzzy v0.1.0
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 // indirect
|
||||
golang.org/x/text v0.3.2
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
gopkg.in/yaml.v2 v2.2.4
|
||||
gotest.tools v2.2.0+incompatible
|
||||
k8s.io/api v0.0.0
|
||||
k8s.io/apiextensions-apiserver v0.0.0
|
||||
k8s.io/apimachinery v0.0.0
|
||||
k8s.io/cli-runtime v0.0.0
|
||||
k8s.io/client-go v0.0.0
|
||||
k8s.io/klog v0.4.0
|
||||
k8s.io/kubectl v0.0.0
|
||||
k8s.io/kubernetes v1.16.3
|
||||
k8s.io/metrics v0.0.0
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787
|
||||
|
|
|
|||
273
go.sum
273
go.sum
|
|
@ -1,7 +1,9 @@
|
|||
bitbucket.org/bertimus9/systemstat v0.0.0-20180207000608-0eeff89b0690/go.mod h1:Ulb78X89vxKYgdL24HMTiXYHlyHEvruOj1ZPlqeNEZM=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
github.com/Azure/azure-sdk-for-go v32.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
|
|
@ -11,33 +13,81 @@ github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZt
|
|||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
|
||||
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
|
||||
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/hcsshim v0.0.0-20190417211021-672e52e9209d/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/Rican7/retry v0.1.0/go.mod h1:FgOROf8P5bebcC1DS0PdOQiqGUridaZvikzUmkFW6gg=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzbY=
|
||||
github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/auth0/go-jwt-middleware v0.0.0-20170425171159-5493cabe49f7/go.mod h1:LWMyo4iOLWXHGdBki7NIht1kHru/0wM179h+d3g8ATM=
|
||||
github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/bazelbuild/bazel-gazelle v0.0.0-20181012220611-c728ce9f663e/go.mod h1:uHBSeeATKpVazAACZBDPL/Nk/UhQDDsJWDlqYJo8/Us=
|
||||
github.com/bazelbuild/buildtools v0.0.0-20180226164855-80c7f0d45d7e/go.mod h1:5JP0TXzWDHXv8qvxRC4InIazwdyDseBDbzESUMKk1yU=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU=
|
||||
github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17OJKJXD2Cfs=
|
||||
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ4uw0RzP1E=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cespare/prettybench v0.0.0-20150116022406-03b8cfe5406c/go.mod h1:Xe6ZsFhtM8HrDku0pxJ3/Lr51rwykrzgFwpmTzleatY=
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||
github.com/checkpoint-restore/go-criu v0.0.0-20190109184317-bdb7599cd87b/go.mod h1:TrMrLQfeENAPYPRsJuq3jsqdlRh3lvi6trTZJG8+tho=
|
||||
github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cfssl v0.0.0-20180726162950-56268a613adf/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313/go.mod h1:P1wt9Z3DP8O6W3rvwCt0REIlshg1InHImaLW0t3ObY0=
|
||||
github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
|
||||
github.com/container-storage-interface/spec v1.1.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4=
|
||||
github.com/containerd/console v0.0.0-20170925154832-84eeaae905fa/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
||||
github.com/containerd/containerd v1.0.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/typeurl v0.0.0-20190228175220-2a93cfde8c20/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||
github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
||||
github.com/coredns/corefile-migration v1.0.2/go.mod h1:OFwBp/Wc9dJt5cAZzHWMNhK1r5L0p0jDwIBc6j8NC8E=
|
||||
github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.17+incompatible h1:f/Z3EoDSx1yjaIjLQGo1diYUlQYSBrrAQ5vP8NjwXwo=
|
||||
github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea h1:n2Ltr3SrfQlf/9nOna1DoGKxLx3qTSI8Ttl6Xrqp6mw=
|
||||
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/rkt v1.30.0/go.mod h1:O634mlH6U7qk87poQifK6M2rsFNt+FyUTWNMnP1hF1U=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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=
|
||||
|
|
@ -45,24 +95,34 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
|
||||
github.com/derailed/tview v0.3.2 h1:By43yu6kbGvA+iL09VAhTKxKEd02BBOtUPIlrkeHxT4=
|
||||
github.com/derailed/tview v0.3.2/go.mod h1:yApPszFU62FoaGkf7swy2nIdV/h7Nid3dhMSVy6+OFI=
|
||||
github.com/derailed/tview v0.3.3 h1:tipPwxcDhx0zRBZuc8VKIrNgWL40FL5JeF/30XVieUE=
|
||||
github.com/derailed/tview v0.3.3/go.mod h1:yApPszFU62FoaGkf7swy2nIdV/h7Nid3dhMSVy6+OFI=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/libnetwork v0.0.0-20180830151422-a9cd636e3789/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s=
|
||||
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
|
|
@ -74,25 +134,66 @@ github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2H
|
|||
github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M=
|
||||
github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/analysis v0.19.2 h1:ophLETFestFZHk3ji7niPEL4d466QjW+0Tdg5VyDq7E=
|
||||
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
|
||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY=
|
||||
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
|
||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.2 h1:rf5ArTHmIJxyV5Oiks+Su0mUens1+AjpkPoWr5xFRcI=
|
||||
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
|
||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
||||
github.com/go-openapi/runtime v0.19.0 h1:sU6pp4dSV2sGlNKKyHxZzi1m1kG4WnYtWcJ+HYbygjE=
|
||||
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
|
||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE=
|
||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.19.0 h1:0Dn9qy1G9+UJfRU7TR8bmdGxb4uifB7HNrJjOnV0yPk=
|
||||
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||
github.com/go-openapi/validate v0.19.2 h1:ky5l57HjyVRrsJfd2+Ro5Z9PjGuKbsmftwyMtk8H7js=
|
||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
||||
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
|
||||
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
|
@ -104,6 +205,8 @@ github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//
|
|||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/cadvisor v0.34.0/go.mod h1:1nql6U13uTHaLYB8rLS5x9IJc2qT6Xd/Tr1sTX6NE48=
|
||||
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
|
|
@ -112,6 +215,9 @@ github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
|||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
|
|
@ -119,46 +225,88 @@ github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhp
|
|||
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
|
||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc h1:f8eY6cV/x1x+HLjOp4r72s/31/V2aTUtg5oKRRPf8/Q=
|
||||
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/heketi/heketi v9.0.0+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o=
|
||||
github.com/heketi/rest v0.0.0-20180404230133-aa6a65207413/go.mod h1:BeS3M108VzVlmAue3lv2WcGuPAX94/KN63MUURzbYSI=
|
||||
github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4=
|
||||
github.com/heketi/utils v0.0.0-20170317161834-435bc5bdfa64/go.mod h1:RYlF4ghFZPPmk2TC5REt5OFwvfb6lzxFWrTWB+qs28s=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
|
||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/karrick/godirwalk v1.7.5/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc=
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
|
||||
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
|
||||
github.com/lpabon/godbc v0.1.1/go.mod h1:Jo9QV0cf3U6jZABgiJ2skINAXb9j8m51r07g4KI92ZA=
|
||||
github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f/go.mod h1:JpH9J1c9oX6otFSgdUHwUBUizmKlrMjxWnIAjff4m04=
|
||||
github.com/lucas-clemente/quic-clients v0.1.0/go.mod h1:y5xVIEoObKqULIKivu+gD/LU90pL73bTdtQjPBvtCBk=
|
||||
github.com/lucas-clemente/quic-go v0.10.2/go.mod h1:hvaRS9IHjFLMq76puFJeWNfmn+H70QZ/CXoxqw9bzao=
|
||||
github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.2 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09mS6CXfO4=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.5 h1:jrGtp51JOKTWgvLFzfG6OtZOJcK2sEnzc/U+zw7TtbA=
|
||||
github.com/mattn/go-runewidth v0.0.5/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mesos/mesos-go v0.0.9/go.mod h1:kPYCMQ9gsOXVAle1OsoY4I1+9kPu8GHkf88aV59fDr4=
|
||||
github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY=
|
||||
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mindprince/gonvml v0.0.0-20171110221305-fee913ce8fb2/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY=
|
||||
github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
|
|
@ -167,15 +315,30 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN
|
|||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mrunalp/fileutils v0.0.0-20160930181131-4ee1cc9a8058/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d h1:7PxY7LVfSZm7PEeBTyK1rj1gABdCO2mbri6GKO1cMDs=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mvdan/xurls v1.1.0/go.mod h1:tQlNn3BED8bE/15hnSL2HLkDeLWpNPAwtw7wkEq44oU=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||
github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v1.0.0-rc2.0.20190611121236-6cc515888830/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runtime-spec v1.0.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.2.2/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
|
||||
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
|
|
@ -187,10 +350,17 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/pquerna/ffjson v0.0.0-20180717144149-af8b230fcd20/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
|
||||
github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/quobyte/api v0.1.2/go.mod h1:jL7lIHrmqQ7yh05OJ+eEEdHr0u/kmT1Ff9iHd+4H6VI=
|
||||
github.com/rakyll/hey v0.1.2 h1:XlGaKcBdmXJaPImiTnE+TGLDUWQ2toYuHCwdrylLjmg=
|
||||
github.com/rakyll/hey v0.1.2/go.mod h1:S5M+++KwbmxA7w68S92B5NdWiCB+cIhITaMUkq9W608=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||
|
|
@ -198,39 +368,75 @@ github.com/rivo/tview v0.0.0-20191018115645-bacbf5155bc1/go.mod h1:+rKjP5+h9HMwW
|
|||
github.com/rivo/uniseg v0.0.0-20190513083848-b9f5b9457d44/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.14.3 h1:4EGfSkR2hJDB0s3oFfrlPqjU1e4WLncergLil3nEKW0=
|
||||
github.com/rs/zerolog v1.14.3/go.mod h1:3WXPzbXEEliJ+a6UFE4vhIxV8qR1EML6ngzP9ug4eYg=
|
||||
github.com/rs/zerolog v1.17.2 h1:RMRHFw2+wF7LO0QqtELQwo8hqSmqISyCJeFeAAuWcRo=
|
||||
github.com/rs/zerolog v1.17.2/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I=
|
||||
github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
|
||||
github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
|
||||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/storageos/go-api v0.0.0-20180912212459-343b3eff91fc/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/syndtr/gocapability v0.0.0-20160928074757-e7cb7fa329f4/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/thecodeteam/goscaleio v0.1.0/go.mod h1:68sdkZAsK8bvEwBlbQnlLS+xU+hvLYM/iQ8KXej1AwM=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
|
||||
github.com/vishvananda/netlink v0.0.0-20171020171820-b2de5d10e38e/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||
github.com/vishvananda/netns v0.0.0-20171111001504-be1fbeda1936/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
|
||||
github.com/vmware/govmomi v0.20.1/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
||||
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 h1:j2hhcujLRHAg872RWAV5yaUrEjHEObwDv3aImCaNLek=
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569 h1:nSQar3Y0E3VQF/VdZ8PTAilaXpER+d7ypdABCrpwMdg=
|
||||
go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df h1:shvkWr0NAZkg4nPuE3XrKP0VuBPijjk3TfX6Y6acFNg=
|
||||
go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15 h1:Z2sc4+v0JHV6Mn4kX1f2a5nruNjmV+Th32sugE8zwz8=
|
||||
go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
|
@ -240,18 +446,28 @@ golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMx
|
|||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181017193950-04a2e542c03f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68=
|
||||
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
|
|
@ -266,12 +482,17 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181004145325-8469e314837c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
|
||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
@ -283,64 +504,113 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20170824195420-5d2fd3ccab98/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.6.1-0.20190607001116-5213b8090861/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/gotestsum v0.3.5/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.2/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
k8s.io/api v0.0.0-20190918155943-95b840bb6a1f h1:8FRUST8oUkEI45WYKyD8ed7Ad0Kg5v11zHyPkEVb2xo=
|
||||
k8s.io/api v0.0.0-20190918155943-95b840bb6a1f/go.mod h1:uWuOHnjmNrtQomJrvEBg0c0HRNyQ+8KTEERVsK0PW48=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783 h1:V6ndwCPoao1yZ52agqOKaUAl7DYWVGiXjV7ePA2i610=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY=
|
||||
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655 h1:CS1tBQz3HOXiseWZu6ZicKX361CZLT97UFnnPx0aqBw=
|
||||
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4=
|
||||
k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad h1:IMoNR9pilTBaCS5WpwWnAdmoVYVeXowOD3bLrwxIAtQ=
|
||||
k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg=
|
||||
k8s.io/cli-runtime v0.0.0-20190918162238-f783a3654da8 h1:W3zT6wRwUKkEGnUu1OAAJFwcgETlCu1BLdNP/VCTFuM=
|
||||
k8s.io/cli-runtime v0.0.0-20190918162238-f783a3654da8/go.mod h1:WRliO+M6Osz7/zdOF0RI42IsJgSYHUwbLgqAWJPneSs=
|
||||
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90 h1:mLmhKUm1X+pXu0zXMEzNsOF5E2kKFGe5o6BZBIIqA6A=
|
||||
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk=
|
||||
k8s.io/cloud-provider v0.0.0-20190918163234-a9c1f33e9fb9/go.mod h1:YfUBehfPUDgnhqAFcuXj8haXt/v86nhy8r4ZOuSvXhg=
|
||||
k8s.io/cluster-bootstrap v0.0.0-20190918163108-da9fdfce26bb/go.mod h1:mQVbtFRxlw/BzBqBaQwIMzjDTST1KrGtzWaR4CGlsTU=
|
||||
k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE=
|
||||
k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090 h1:0UWOjjag5IcVoAko0g+3qGhegdwWkRf4v4AHCIMVwnc=
|
||||
k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA=
|
||||
k8s.io/cri-api v0.0.0-20190828162817-608eb1dad4ac/go.mod h1:BvtUaNBr0fEpzb11OfrQiJLsLPtqbmulpo1fPwcpP6Q=
|
||||
k8s.io/csi-translation-lib v0.0.0-20190918163402-db86a8c7bb21/go.mod h1:Ja9f0K9MkTuUSyBgpjFt2am69TOjrmkQUN25WTF3CCM=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/heapster v1.2.0-beta.1/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.4.0 h1:lCJCxf/LIowc2IGS9TPjWDyXY4nOmdGdfcwwDQCOURQ=
|
||||
k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-aggregator v0.0.0-20190918161219-8c8f079fddc3/go.mod h1:NJisPUqwlg1A99RhO1BTnNtwC4pKUyXJ2f3Xc4PxKQg=
|
||||
k8s.io/kube-controller-manager v0.0.0-20190918162944-7a93a0ddadd8/go.mod h1:+HrHoqJm0UqnlrBEKXGzs2701YN4+ozi76oG7iYvJ8s=
|
||||
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf h1:EYm5AW/UUDbnmnI+gK0TJDVK9qPLhM+sRHYanNKw0EQ=
|
||||
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/kube-proxy v0.0.0-20190918162534-de037b596c1e/go.mod h1:/48p8Y6dkWJrll4tsceAoGKudGpRmtQu/u1zlG14NnI=
|
||||
k8s.io/kube-scheduler v0.0.0-20190918162820-3b5c1246eb18/go.mod h1:k2dnGirIGylr51dpqxn2Zv6Yt47A+6NiynBIYfAU67I=
|
||||
k8s.io/kubectl v0.0.0-20190918164019-21692a0861df h1:EwjdCG4HveZxJkI650+g4UoIuSvH7vODn55VmBjxIAo=
|
||||
k8s.io/kubectl v0.0.0-20190918164019-21692a0861df/go.mod h1:AjffgL1ZYSrbpRJHER9vC+/INYwTSdmoZD0DXhMKzxQ=
|
||||
k8s.io/kubelet v0.0.0-20190918162654-250a1838aa2c/go.mod h1:LGhpyzd/3AkWcFcQJ3yO1UxMnJ6urMkCYfCp4iVxhjs=
|
||||
k8s.io/kubernetes v1.16.3 h1:Bk2cKOdTtuGeod3+ytBeXxqIVHbh7Pu+aq0c+YJLX7g=
|
||||
k8s.io/kubernetes v1.16.3/go.mod h1:hJd0X6w7E/MiE7PcDp11XHhdgQBYc33vP+WtTJqG/AU=
|
||||
k8s.io/legacy-cloud-providers v0.0.0-20190918163543-cfa506e53441/go.mod h1:Phw/j+7dcoTPXRkv9Nyi3RJuA6SVSoHlc7M5K1pHizM=
|
||||
k8s.io/metrics v0.0.0-20190918162108-227c654b2546 h1:GmR5FKUvbcVV2TLAVFusUFWENjlIg7KLldAST5DqalY=
|
||||
k8s.io/metrics v0.0.0-20190918162108-227c654b2546/go.mod h1:XUFuIsGbIqaUga6Ivs02cCzxNjY4RPRvYnW0KhmnpQY=
|
||||
k8s.io/repo-infra v0.0.0-20181204233714-00fe14e3d1a3/go.mod h1:+G1xBfZDfVFsm1Tj/HNCvg4QqWx8rJ2Fxpqr1rqp/gQ=
|
||||
k8s.io/sample-apiserver v0.0.0-20190918161442-d4c9c65c82af/go.mod h1:HP/BmiRyZTMIZ5RI2p4tCz/b2kre7URuKLQ7/KHqWAs=
|
||||
k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE=
|
||||
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
|
||||
|
|
@ -351,7 +621,10 @@ modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
|||
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca h1:6dsH6AYQWbyZmtttJNe8Gq1cXOeS1BdV3eW37zHilAQ=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
|
||||
vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787 h1:O69FD9pJA4WUZlEwYatBEEkRWKQ5cKodWpdKTrCS/iQ=
|
||||
vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
|
||||
|
|
|
|||
|
|
@ -1,22 +1,17 @@
|
|||
package k8s
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/discovery/cached/disk"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
authorizationv1 "k8s.io/api/authorization/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/client-go/discovery/cached/disk"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
|
|
@ -29,116 +24,78 @@ const NA = "n/a"
|
|||
|
||||
var supportedMetricsAPIVersions = []string{"v1beta1"}
|
||||
|
||||
type (
|
||||
// Collection of empty interfaces.
|
||||
Collection []interface{}
|
||||
// Authorizer checks what a user can or cannot do to a resource.
|
||||
type Authorizer interface {
|
||||
// CanI returns true if the user can use these actions for a given resource.
|
||||
CanI(ns, gvr string, verbs []string) (bool, error)
|
||||
}
|
||||
|
||||
// Cruder represent a crudable Kubernetes resource.
|
||||
Cruder interface {
|
||||
Get(ns string, name string) (interface{}, error)
|
||||
List(ns string) (Collection, error)
|
||||
Delete(ns string, name string) error
|
||||
SetFieldSelector(string)
|
||||
SetLabelSelector(string)
|
||||
}
|
||||
// BOZO!! Refactor!
|
||||
// Connection represents a Kubenetes apiserver connection.
|
||||
type Connection interface {
|
||||
Authorizer
|
||||
|
||||
// Connection represents a Kubenetes apiserver connection.
|
||||
Connection interface {
|
||||
Config() *Config
|
||||
DialOrDie() kubernetes.Interface
|
||||
SwitchContextOrDie(ctx string)
|
||||
NSDialOrDie() dynamic.NamespaceableResourceInterface
|
||||
CachedDiscovery() (*disk.CachedDiscoveryClient, error)
|
||||
RestConfigOrDie() *restclient.Config
|
||||
MXDial() (*versioned.Clientset, error)
|
||||
DynDialOrDie() dynamic.Interface
|
||||
HasMetrics() bool
|
||||
IsNamespaced(n string) bool
|
||||
SupportsResource(group string) bool
|
||||
ValidNamespaces() ([]v1.Namespace, error)
|
||||
NodePods(node string) (*v1.PodList, error)
|
||||
SupportsRes(grp string, versions []string) (string, bool, error)
|
||||
ServerVersion() (*version.Info, error)
|
||||
FetchNodes() (*v1.NodeList, error)
|
||||
CurrentNamespaceName() (string, error)
|
||||
CheckNSAccess(ns string) error
|
||||
CheckListNSAccess() error
|
||||
CanIAccess(ns, rvg string, verbs []string) (bool, error)
|
||||
}
|
||||
Config() *Config
|
||||
DialOrDie() kubernetes.Interface
|
||||
SwitchContextOrDie(ctx string)
|
||||
NSDialOrDie() dynamic.NamespaceableResourceInterface
|
||||
CachedDiscovery() (*disk.CachedDiscoveryClient, error)
|
||||
RestConfigOrDie() *restclient.Config
|
||||
MXDial() (*versioned.Clientset, error)
|
||||
DynDialOrDie() dynamic.Interface
|
||||
HasMetrics() bool
|
||||
IsNamespaced(n string) bool
|
||||
SupportsResource(group string) bool
|
||||
ValidNamespaces() ([]v1.Namespace, error)
|
||||
SupportsRes(grp string, versions []string) (string, bool, error)
|
||||
ServerVersion() (*version.Info, error)
|
||||
FetchNodes() (*v1.NodeList, error)
|
||||
CurrentNamespaceName() (string, error)
|
||||
}
|
||||
|
||||
k8sClient struct {
|
||||
client kubernetes.Interface
|
||||
dClient dynamic.Interface
|
||||
nsClient dynamic.NamespaceableResourceInterface
|
||||
mxsClient *versioned.Clientset
|
||||
}
|
||||
|
||||
// APIClient represents a Kubernetes api client.
|
||||
APIClient struct {
|
||||
k8sClient
|
||||
|
||||
cachedDiscovery *disk.CachedDiscoveryClient
|
||||
config *Config
|
||||
useMetricServer bool
|
||||
log zerolog.Logger
|
||||
mx sync.Mutex
|
||||
}
|
||||
)
|
||||
// APIClient represents a Kubernetes api client.
|
||||
type APIClient struct {
|
||||
client kubernetes.Interface
|
||||
dClient dynamic.Interface
|
||||
nsClient dynamic.NamespaceableResourceInterface
|
||||
mxsClient *versioned.Clientset
|
||||
cachedDiscovery *disk.CachedDiscoveryClient
|
||||
config *Config
|
||||
useMetricServer bool
|
||||
mx sync.Mutex
|
||||
}
|
||||
|
||||
// InitConnectionOrDie initialize connection from command line args.
|
||||
// Checks for connectivity with the api server.
|
||||
func InitConnectionOrDie(config *Config, logger zerolog.Logger) *APIClient {
|
||||
conn := APIClient{config: config, log: logger}
|
||||
func InitConnectionOrDie(config *Config) *APIClient {
|
||||
conn := APIClient{config: config}
|
||||
conn.useMetricServer = conn.supportsMxServer()
|
||||
|
||||
return &conn
|
||||
}
|
||||
|
||||
// CheckListNSAccess check if current user can list namespaces.
|
||||
func (a *APIClient) CheckListNSAccess() error {
|
||||
ns := NewNamespace(a)
|
||||
_, err := ns.List("", metav1.ListOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
// CheckNSAccess asserts if user can access a namespace.
|
||||
func (a *APIClient) CheckNSAccess(n string) error {
|
||||
ns := NewNamespace(a)
|
||||
if n == "" {
|
||||
_, err := ns.List(n, metav1.ListOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := ns.Get("", n)
|
||||
return err
|
||||
}
|
||||
|
||||
func makeSAR(ns, rvg string) *authorizationv1.SelfSubjectAccessReview {
|
||||
gvr, _ := schema.ParseResourceArg(strings.ToLower(rvg))
|
||||
if gvr == nil {
|
||||
panic(fmt.Errorf("Unable to get GVR from url %s", rvg))
|
||||
}
|
||||
log.Debug().Msgf("GVR for %s -- %#v", rvg, *gvr)
|
||||
func makeSAR(ns, gvr string) *authorizationv1.SelfSubjectAccessReview {
|
||||
res := GVR(gvr).AsGVR()
|
||||
return &authorizationv1.SelfSubjectAccessReview{
|
||||
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationv1.ResourceAttributes{
|
||||
Namespace: ns,
|
||||
Group: gvr.Group,
|
||||
Resource: gvr.Resource,
|
||||
Group: res.Group,
|
||||
Resource: res.Resource,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CanIAccess checks if user has access to a certain resource.
|
||||
func (a *APIClient) CanIAccess(ns, rvg string, verbs []string) (bool, error) {
|
||||
sar := makeSAR(ns, rvg)
|
||||
// CanI checks if user has access to a certain resource.
|
||||
func (a *APIClient) CanI(ns, gvr string, verbs []string) (bool, error) {
|
||||
sar := makeSAR(ns, gvr)
|
||||
dial := a.DialOrDie().AuthorizationV1().SelfSubjectAccessReviews()
|
||||
for _, v := range verbs {
|
||||
sar.Spec.ResourceAttributes.Verb = v
|
||||
resp, err := dial.Create(sar)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("CanIAccess")
|
||||
log.Error().Err(err).Msgf("CanI")
|
||||
return false, err
|
||||
}
|
||||
if !resp.Status.Allowed {
|
||||
|
|
@ -177,19 +134,6 @@ func (a *APIClient) ValidNamespaces() ([]v1.Namespace, error) {
|
|||
return nn.Items, nil
|
||||
}
|
||||
|
||||
// NodePods returns a collection of all available pods on a given node.
|
||||
func (a *APIClient) NodePods(node string) (*v1.PodList, error) {
|
||||
const selFmt = "spec.nodeName=%s,status.phase!=%s,status.phase!=%s"
|
||||
fieldSelector, err := fields.ParseSelector(fmt.Sprintf(selFmt, node, v1.PodSucceeded, v1.PodFailed))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a.DialOrDie().CoreV1().Pods("").List(metav1.ListOptions{
|
||||
FieldSelector: fieldSelector.String(),
|
||||
})
|
||||
}
|
||||
|
||||
// IsNamespaced check on server if given resource is namespaced
|
||||
func (a *APIClient) IsNamespaced(res string) bool {
|
||||
discovery, err := a.CachedDiscovery()
|
||||
|
|
@ -247,7 +191,7 @@ func (a *APIClient) DialOrDie() kubernetes.Interface {
|
|||
|
||||
var err error
|
||||
if a.client, err = kubernetes.NewForConfig(a.RestConfigOrDie()); err != nil {
|
||||
a.log.Fatal().Msgf("Unable to connect to api server %v", err)
|
||||
log.Fatal().Msgf("Unable to connect to api server %v", err)
|
||||
}
|
||||
return a.client
|
||||
}
|
||||
|
|
@ -256,7 +200,7 @@ func (a *APIClient) DialOrDie() kubernetes.Interface {
|
|||
func (a *APIClient) RestConfigOrDie() *restclient.Config {
|
||||
cfg, err := a.config.RESTConfig()
|
||||
if err != nil {
|
||||
a.log.Panic().Msgf("Unable to connect to api server %v", err)
|
||||
log.Panic().Msgf("Unable to connect to api server %v", err)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
|
@ -286,7 +230,7 @@ func (a *APIClient) DynDialOrDie() dynamic.Interface {
|
|||
|
||||
var err error
|
||||
if a.dClient, err = dynamic.NewForConfig(a.RestConfigOrDie()); err != nil {
|
||||
a.log.Panic().Err(err)
|
||||
log.Panic().Err(err)
|
||||
}
|
||||
return a.dClient
|
||||
}
|
||||
|
|
@ -318,7 +262,7 @@ func (a *APIClient) MXDial() (*versioned.Clientset, error) {
|
|||
}
|
||||
var err error
|
||||
if a.mxsClient, err = versioned.NewForConfig(a.RestConfigOrDie()); err != nil {
|
||||
a.log.Error().Err(err)
|
||||
log.Error().Err(err)
|
||||
}
|
||||
|
||||
return a.mxsClient, err
|
||||
|
|
@ -328,14 +272,14 @@ func (a *APIClient) MXDial() (*versioned.Clientset, error) {
|
|||
func (a *APIClient) SwitchContextOrDie(ctx string) {
|
||||
currentCtx, err := a.config.CurrentContextName()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal().Err(err).Msg("Fetching current context")
|
||||
}
|
||||
|
||||
if currentCtx != ctx {
|
||||
a.cachedDiscovery = nil
|
||||
a.reset()
|
||||
if err := a.config.SwitchContext(ctx); err != nil {
|
||||
panic(err)
|
||||
log.Fatal().Err(err).Msg("Switching context")
|
||||
}
|
||||
a.useMetricServer = a.supportsMxServer()
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
package k8s
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
|
@ -12,8 +13,6 @@ import (
|
|||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
const defaultNamespace = "default"
|
||||
|
||||
// Config tracks a kubernetes configuration.
|
||||
type Config struct {
|
||||
flags *genericclioptions.ConfigFlags
|
||||
|
|
@ -21,11 +20,15 @@ type Config struct {
|
|||
currentContext string
|
||||
rawConfig *clientcmdapi.Config
|
||||
restConfig *restclient.Config
|
||||
mutex *sync.RWMutex
|
||||
}
|
||||
|
||||
// NewConfig returns a new k8s config or an error if the flags are invalid.
|
||||
func NewConfig(f *genericclioptions.ConfigFlags) *Config {
|
||||
return &Config{flags: f}
|
||||
return &Config{
|
||||
flags: f,
|
||||
mutex: &sync.RWMutex{},
|
||||
}
|
||||
}
|
||||
|
||||
// Flags returns configuration flags.
|
||||
|
|
@ -233,12 +236,18 @@ func (c *Config) NamespaceNames(nns []v1.Namespace) []string {
|
|||
|
||||
// ConfigAccess return the current kubeconfig api server access configuration.
|
||||
func (c *Config) ConfigAccess() (clientcmd.ConfigAccess, error) {
|
||||
c.mutex.RLock()
|
||||
defer c.mutex.RUnlock()
|
||||
|
||||
c.ensureConfig()
|
||||
return c.clientConfig.ConfigAccess(), nil
|
||||
}
|
||||
|
||||
// RawConfig fetch the current kubeconfig with no overrides.
|
||||
func (c *Config) RawConfig() (clientcmdapi.Config, error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if c.rawConfig != nil {
|
||||
if c.rawConfig.CurrentContext == c.currentContext {
|
||||
return *c.rawConfig, nil
|
||||
|
|
@ -283,7 +292,6 @@ func (c *Config) ensureConfig() {
|
|||
|
||||
log.Debug().Msg("Loading raw config from flags...")
|
||||
c.clientConfig = c.flags.ToRawKubeConfigLoader()
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
package k8s_test
|
||||
package client_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
|
@ -28,7 +28,7 @@ func TestConfigCurrentContext(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, u := range uu {
|
||||
cfg := k8s.NewConfig(u.flags)
|
||||
cfg := client.NewConfig(u.flags)
|
||||
ctx, err := cfg.CurrentContextName()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, u.context, ctx)
|
||||
|
|
@ -46,7 +46,7 @@ func TestConfigCurrentCluster(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, u := range uu {
|
||||
cfg := k8s.NewConfig(u.flags)
|
||||
cfg := client.NewConfig(u.flags)
|
||||
ctx, err := cfg.CurrentClusterName()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, u.cluster, ctx)
|
||||
|
|
@ -64,7 +64,7 @@ func TestConfigCurrentUser(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, u := range uu {
|
||||
cfg := k8s.NewConfig(u.flags)
|
||||
cfg := client.NewConfig(u.flags)
|
||||
ctx, err := cfg.CurrentUserName()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, u.user, ctx)
|
||||
|
|
@ -83,7 +83,7 @@ func TestConfigCurrentNamespace(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, u := range uu {
|
||||
cfg := k8s.NewConfig(u.flags)
|
||||
cfg := client.NewConfig(u.flags)
|
||||
ns, err := cfg.CurrentNamespaceName()
|
||||
assert.Equal(t, u.err, err)
|
||||
assert.Equal(t, u.namespace, ns)
|
||||
|
|
@ -102,7 +102,7 @@ func TestConfigGetContext(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, u := range uu {
|
||||
cfg := k8s.NewConfig(u.flags)
|
||||
cfg := client.NewConfig(u.flags)
|
||||
ctx, err := cfg.GetContext(u.cluster)
|
||||
if err != nil {
|
||||
assert.Equal(t, u.err, err)
|
||||
|
|
@ -120,7 +120,7 @@ func TestConfigSwitchContext(t *testing.T) {
|
|||
ClusterName: &cluster,
|
||||
}
|
||||
|
||||
cfg := k8s.NewConfig(&flags)
|
||||
cfg := client.NewConfig(&flags)
|
||||
err := cfg.SwitchContext("blee")
|
||||
assert.Nil(t, err)
|
||||
ctx, err := cfg.CurrentContextName()
|
||||
|
|
@ -135,7 +135,7 @@ func TestConfigClusterNameFromContext(t *testing.T) {
|
|||
ClusterName: &cluster,
|
||||
}
|
||||
|
||||
cfg := k8s.NewConfig(&flags)
|
||||
cfg := client.NewConfig(&flags)
|
||||
cl, err := cfg.ClusterNameFromContext("blee")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "blee", cl)
|
||||
|
|
@ -148,7 +148,7 @@ func TestConfigAccess(t *testing.T) {
|
|||
ClusterName: &cluster,
|
||||
}
|
||||
|
||||
cfg := k8s.NewConfig(&flags)
|
||||
cfg := client.NewConfig(&flags)
|
||||
acc, err := cfg.ConfigAccess()
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, len(acc.GetDefaultFilename()) > 0)
|
||||
|
|
@ -161,7 +161,7 @@ func TestConfigContexts(t *testing.T) {
|
|||
ClusterName: &cluster,
|
||||
}
|
||||
|
||||
cfg := k8s.NewConfig(&flags)
|
||||
cfg := client.NewConfig(&flags)
|
||||
cc, err := cfg.Contexts()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, len(cc))
|
||||
|
|
@ -174,7 +174,7 @@ func TestConfigContextNames(t *testing.T) {
|
|||
ClusterName: &cluster,
|
||||
}
|
||||
|
||||
cfg := k8s.NewConfig(&flags)
|
||||
cfg := client.NewConfig(&flags)
|
||||
cc, err := cfg.ContextNames()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, len(cc))
|
||||
|
|
@ -187,7 +187,7 @@ func TestConfigClusterNames(t *testing.T) {
|
|||
ClusterName: &cluster,
|
||||
}
|
||||
|
||||
cfg := k8s.NewConfig(&flags)
|
||||
cfg := client.NewConfig(&flags)
|
||||
cc, err := cfg.ClusterNames()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, len(cc))
|
||||
|
|
@ -200,7 +200,7 @@ func TestConfigDelContext(t *testing.T) {
|
|||
ClusterName: &cluster,
|
||||
}
|
||||
|
||||
cfg := k8s.NewConfig(&flags)
|
||||
cfg := client.NewConfig(&flags)
|
||||
err := cfg.DelContext("fred")
|
||||
assert.Nil(t, err)
|
||||
cc, err := cfg.ContextNames()
|
||||
|
|
@ -214,7 +214,7 @@ func TestConfigRestConfig(t *testing.T) {
|
|||
KubeConfig: &kubeConfig,
|
||||
}
|
||||
|
||||
cfg := k8s.NewConfig(&flags)
|
||||
cfg := client.NewConfig(&flags)
|
||||
rc, err := cfg.RESTConfig()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "https://localhost:3000", rc.Host)
|
||||
|
|
@ -226,7 +226,7 @@ func TestConfigBadConfig(t *testing.T) {
|
|||
KubeConfig: &kubeConfig,
|
||||
}
|
||||
|
||||
cfg := k8s.NewConfig(&flags)
|
||||
cfg := client.NewConfig(&flags)
|
||||
_, err := cfg.RESTConfig()
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
|
@ -238,7 +238,7 @@ func TestNamespaceNames(t *testing.T) {
|
|||
KubeConfig: &kubeConfig,
|
||||
}
|
||||
|
||||
cfg := k8s.NewConfig(&flags)
|
||||
cfg := client.NewConfig(&flags)
|
||||
|
||||
nn := []v1.Namespace{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "ns1"}},
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"vbom.ml/util/sortorder"
|
||||
)
|
||||
|
||||
// GVR represents a kubernetes resource schema as a string.
|
||||
// Format is group/version/resources
|
||||
type GVR string
|
||||
|
||||
// NewGVR builds a new gvr from a group, version, resource.
|
||||
func NewGVR(g, v, r string) GVR {
|
||||
return GVR(path.Join(g, v, r))
|
||||
}
|
||||
|
||||
// FromGVAndR builds a gvr from a group/version and resource.
|
||||
func FromGVAndR(gv, r string) GVR {
|
||||
return GVR(path.Join(gv, r))
|
||||
}
|
||||
|
||||
// ResName returns a resource . separated descriptor in the shape of kind.version.group.
|
||||
func (g GVR) ResName() string {
|
||||
return g.ToR() + "." + g.ToV() + "." + g.ToG()
|
||||
}
|
||||
|
||||
// String returns gvr as string.
|
||||
func (g GVR) String() string {
|
||||
return string(g)
|
||||
}
|
||||
|
||||
// AsGV returns the group version scheme representation.
|
||||
func (g GVR) AsGV() schema.GroupVersion {
|
||||
return schema.GroupVersion{
|
||||
Group: g.ToG(),
|
||||
Version: g.ToV(),
|
||||
}
|
||||
}
|
||||
|
||||
// AsGVR returns a a full schema representation.
|
||||
func (g GVR) AsGVR() schema.GroupVersionResource {
|
||||
return schema.GroupVersionResource{
|
||||
Group: g.ToG(),
|
||||
Version: g.ToV(),
|
||||
Resource: g.ToR(),
|
||||
}
|
||||
}
|
||||
|
||||
// ToV returns the resource version.
|
||||
func (g GVR) ToV() string {
|
||||
tokens := strings.Split(string(g), "/")
|
||||
if len(tokens) < 2 {
|
||||
return ""
|
||||
}
|
||||
return tokens[len(tokens)-2]
|
||||
}
|
||||
|
||||
func (g GVR) ToRAndG() (string, string) {
|
||||
tokens := strings.Split(string(g), "/")
|
||||
switch len(tokens) {
|
||||
case 3:
|
||||
return tokens[2], tokens[0]
|
||||
case 2:
|
||||
return tokens[1], "core"
|
||||
default:
|
||||
return tokens[0], "core"
|
||||
}
|
||||
}
|
||||
|
||||
// ToR returns the resource name.
|
||||
func (g GVR) ToR() string {
|
||||
tokens := strings.Split(string(g), "/")
|
||||
return tokens[len(tokens)-1]
|
||||
}
|
||||
|
||||
// ToG returns the resource group name.
|
||||
func (g GVR) ToG() string {
|
||||
tokens := strings.Split(string(g), "/")
|
||||
switch len(tokens) {
|
||||
case 3:
|
||||
return tokens[0]
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
type GVRs []GVR
|
||||
|
||||
func (g GVRs) Len() int {
|
||||
return len(g)
|
||||
}
|
||||
|
||||
func (g GVRs) Swap(i, j int) {
|
||||
g[i], g[j] = g[j], g[i]
|
||||
}
|
||||
|
||||
func (g GVRs) Less(i, j int) bool {
|
||||
g1, g2 := g[i].ToG(), g[j].ToG()
|
||||
|
||||
return sortorder.NaturalLess(g1, g2)
|
||||
}
|
||||
|
||||
// Helper...
|
||||
|
||||
// Can determines the available actions for a given resource.
|
||||
func Can(verbs []string, v string) bool {
|
||||
for _, verb := range verbs {
|
||||
candidates, err := mapVerb(v)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("verb mapping failed")
|
||||
return false
|
||||
}
|
||||
for _, c := range candidates {
|
||||
if verb == c {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func mapVerb(v string) ([]string, error) {
|
||||
switch v {
|
||||
case "describe":
|
||||
return []string{"get"}, nil
|
||||
case "view":
|
||||
return []string{"get", "list"}, nil
|
||||
case "delete":
|
||||
return []string{"delete"}, nil
|
||||
case "edit":
|
||||
return []string{"patch", "update"}, nil
|
||||
default:
|
||||
return []string{}, fmt.Errorf("no standard verb for %q", v)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
package client_test
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func TestGVRSort(t *testing.T) {
|
||||
gg := client.GVRs{"v1/pods", "v1/services", "apps/v1/deployments"}
|
||||
sort.Sort(gg)
|
||||
assert.Equal(t, client.GVRs{"v1/pods", "v1/services", "apps/v1/deployments"}, gg)
|
||||
}
|
||||
|
||||
func TestGVRCan(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
vv []string
|
||||
v string
|
||||
e bool
|
||||
}{
|
||||
"describe": {[]string{"get"}, "describe", true},
|
||||
"view": {[]string{"get", "list", "watch"}, "view", true},
|
||||
"delete": {[]string{"delete", "list", "watch"}, "delete", true},
|
||||
"no_delete": {[]string{"get", "list", "watch"}, "delete", false},
|
||||
"edit": {[]string{"path", "update", "watch"}, "edit", true},
|
||||
"no_edit": {[]string{"get", "list", "watch"}, "edit", false},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.Can(u.vv, u.v))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsGVR(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
gvr string
|
||||
e schema.GroupVersionResource
|
||||
}{
|
||||
"full": {"apps/v1/deployments", schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}},
|
||||
"core": {"v1/pods", schema.GroupVersionResource{Version: "v1", Resource: "pods"}},
|
||||
"bork": {"users", schema.GroupVersionResource{Resource: "users"}},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.GVR(u.gvr).AsGVR())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsGV(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
gvr string
|
||||
e schema.GroupVersion
|
||||
}{
|
||||
"full": {"apps/v1/deployments", schema.GroupVersion{Group: "apps", Version: "v1"}},
|
||||
"core": {"v1/pods", schema.GroupVersion{Version: "v1"}},
|
||||
"bork": {"users", schema.GroupVersion{}},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.GVR(u.gvr).AsGV())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewGVR(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
g, v, r string
|
||||
e string
|
||||
}{
|
||||
"full": {"apps", "v1", "deployments", "apps/v1/deployments"},
|
||||
"core": {"", "v1", "pods", "v1/pods"},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.NewGVR(u.g, u.v, u.r).String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResName(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
gvr string
|
||||
e string
|
||||
}{
|
||||
"full": {"apps/v1/deployments", "deployments.v1.apps"},
|
||||
"core": {"v1/pods", "pods.v1."},
|
||||
"k9s": {"users", "users.."},
|
||||
"empty": {"", ".."},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.GVR(u.gvr).ResName())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToR(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
gvr string
|
||||
e string
|
||||
}{
|
||||
"full": {"apps/v1/deployments", "deployments"},
|
||||
"core": {"v1/pods", "pods"},
|
||||
"k9s": {"users", "users"},
|
||||
"empty": {"", ""},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.GVR(u.gvr).ToR())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToG(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
gvr string
|
||||
e string
|
||||
}{
|
||||
"full": {"apps/v1/deployments", "apps"},
|
||||
"core": {"v1/pods", ""},
|
||||
"k9s": {"users", ""},
|
||||
"empty": {"", ""},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.GVR(u.gvr).ToG())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToV(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
gvr string
|
||||
e string
|
||||
}{
|
||||
"full": {"apps/v1/deployments", "v1"},
|
||||
"core": {"v1beta1/pods", "v1beta1"},
|
||||
"k9s": {"users", ""},
|
||||
"empty": {"", ""},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.GVR(u.gvr).ToV())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToString(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
gvr string
|
||||
}{
|
||||
"full": {"apps/v1/deployments"},
|
||||
"core": {"v1beta1/pods"},
|
||||
"k9s": {"users"},
|
||||
"empty": {""},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.gvr, client.GVR(u.gvr).String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package client_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNamespaced(t *testing.T) {
|
||||
uu := []struct {
|
||||
p, ns, n string
|
||||
}{
|
||||
{"fred/blee", "fred", "blee"},
|
||||
{"blee", "", "blee"},
|
||||
}
|
||||
|
||||
for _, u := range uu {
|
||||
ns, n := client.Namespaced(u.p)
|
||||
assert.Equal(t, u.ns, ns)
|
||||
assert.Equal(t, u.n, n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFQN(t *testing.T) {
|
||||
uu := []struct {
|
||||
ns, n string
|
||||
e string
|
||||
}{
|
||||
{"fred", "blee", "fred/blee"},
|
||||
{"", "blee", "blee"},
|
||||
}
|
||||
|
||||
for _, u := range uu {
|
||||
assert.Equal(t, u.e, client.FQN(u.ns, u.n))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"os/user"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var toFileName = regexp.MustCompile(`[^(\w/\.)]`)
|
||||
|
||||
// Namespaced converts a resource path to namespace and resource name.
|
||||
func Namespaced(p string) (string, string) {
|
||||
ns, n := path.Split(p)
|
||||
|
||||
return strings.Trim(ns, "/"), n
|
||||
}
|
||||
|
||||
// FQN returns a fully qualified resource name.
|
||||
func FQN(ns, n string) string {
|
||||
if ns == "" {
|
||||
return n
|
||||
}
|
||||
return ns + "/" + n
|
||||
}
|
||||
|
||||
func mustHomeDir() string {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Die getting user home directory")
|
||||
}
|
||||
return usr.HomeDir
|
||||
}
|
||||
|
||||
func toHostDir(host string) string {
|
||||
h := strings.Replace(strings.Replace(host, "https://", "", 1), "http://", "", 1)
|
||||
return toFileName.ReplaceAllString(h, "_")
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package k8s
|
||||
package client
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
||||
|
|
@ -9,7 +11,6 @@ import (
|
|||
type (
|
||||
// MetricsServer serves cluster metrics for nodes and pods.
|
||||
MetricsServer struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
|
|
@ -45,47 +46,44 @@ type (
|
|||
|
||||
// NewMetricsServer return a metric server instance.
|
||||
func NewMetricsServer(c Connection) *MetricsServer {
|
||||
return &MetricsServer{&base{}, c}
|
||||
return &MetricsServer{Connection: c}
|
||||
}
|
||||
|
||||
// NodesMetrics retrieves metrics for a given set of nodes.
|
||||
func (m *MetricsServer) NodesMetrics(nodes Collection, metrics *mv1beta1.NodeMetricsList, mmx NodesMetrics) {
|
||||
for _, n := range nodes {
|
||||
no := n.(*v1.Node)
|
||||
func (m *MetricsServer) NodesMetrics(nodes *v1.NodeList, metrics *mv1beta1.NodeMetricsList, mmx NodesMetrics) {
|
||||
for _, no := range nodes.Items {
|
||||
mmx[no.Name] = NodeMetrics{
|
||||
AvailCPU: no.Status.Allocatable.Cpu().MilliValue(),
|
||||
AvailMEM: ToMB(no.Status.Allocatable.Memory().Value()),
|
||||
AvailMEM: toMB(no.Status.Allocatable.Memory().Value()),
|
||||
TotalCPU: no.Status.Capacity.Cpu().MilliValue(),
|
||||
TotalMEM: ToMB(no.Status.Capacity.Memory().Value()),
|
||||
TotalMEM: toMB(no.Status.Capacity.Memory().Value()),
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range metrics.Items {
|
||||
if mx, ok := mmx[c.Name]; ok {
|
||||
mx.CurrentCPU = c.Usage.Cpu().MilliValue()
|
||||
mx.CurrentMEM = ToMB(c.Usage.Memory().Value())
|
||||
mx.CurrentMEM = toMB(c.Usage.Memory().Value())
|
||||
mmx[c.Name] = mx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ClusterLoad retrieves all cluster nodes metrics.
|
||||
func (m *MetricsServer) ClusterLoad(nos Collection, nmx Collection, mx *ClusterMetrics) {
|
||||
nodeMetrics := make(NodesMetrics, len(nos))
|
||||
for _, n := range nos {
|
||||
no := n.(*v1.Node)
|
||||
func (m *MetricsServer) ClusterLoad(nos *v1.NodeList, nmx *mv1beta1.NodeMetricsList, mx *ClusterMetrics) error {
|
||||
nodeMetrics := make(NodesMetrics, len(nos.Items))
|
||||
for _, no := range nos.Items {
|
||||
nodeMetrics[no.Name] = NodeMetrics{
|
||||
AvailCPU: no.Status.Allocatable.Cpu().MilliValue(),
|
||||
AvailMEM: ToMB(no.Status.Allocatable.Memory().Value()),
|
||||
AvailMEM: toMB(no.Status.Allocatable.Memory().Value()),
|
||||
}
|
||||
}
|
||||
|
||||
for _, mx := range nmx {
|
||||
mxx := mx.(*mv1beta1.NodeMetrics)
|
||||
if m, ok := nodeMetrics[mxx.Name]; ok {
|
||||
m.CurrentCPU = mxx.Usage.Cpu().MilliValue()
|
||||
m.CurrentMEM = ToMB(mxx.Usage.Memory().Value())
|
||||
nodeMetrics[mxx.Name] = m
|
||||
for _, mx := range nmx.Items {
|
||||
if m, ok := nodeMetrics[mx.Name]; ok {
|
||||
m.CurrentCPU = mx.Usage.Cpu().MilliValue()
|
||||
m.CurrentMEM = toMB(mx.Usage.Memory().Value())
|
||||
nodeMetrics[mx.Name] = m
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,8 +94,9 @@ func (m *MetricsServer) ClusterLoad(nos Collection, nmx Collection, mx *ClusterM
|
|||
mem += mx.CurrentMEM
|
||||
tmem += mx.AvailMEM
|
||||
}
|
||||
|
||||
mx.PercCPU, mx.PercMEM = toPerc(cpu, tcpu), toPerc(mem, tmem)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FetchNodesMetrics return all metrics for pods in a given namespace.
|
||||
|
|
@ -120,6 +119,16 @@ func (m *MetricsServer) FetchPodsMetrics(ns string) (*mv1beta1.PodMetricsList, e
|
|||
return client.MetricsV1beta1().PodMetricses(ns).List(metav1.ListOptions{})
|
||||
}
|
||||
|
||||
// FetchPodsMetrics return all metrics for pods in a given namespace.
|
||||
func (m *MetricsServer) FetchPodMetrics(ns, sel string) (*mv1beta1.PodMetrics, error) {
|
||||
client, err := m.MXDial()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client.MetricsV1beta1().PodMetricses(ns).Get(sel, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// PodsMetrics retrieves metrics for all pods in a given namespace.
|
||||
func (m *MetricsServer) PodsMetrics(pods *mv1beta1.PodMetricsList, mmx PodsMetrics) {
|
||||
// Compute all pod's containers metrics.
|
||||
|
|
@ -127,8 +136,25 @@ func (m *MetricsServer) PodsMetrics(pods *mv1beta1.PodMetricsList, mmx PodsMetri
|
|||
var mx PodMetrics
|
||||
for _, c := range p.Containers {
|
||||
mx.CurrentCPU += c.Usage.Cpu().MilliValue()
|
||||
mx.CurrentMEM += ToMB(c.Usage.Memory().Value())
|
||||
mx.CurrentMEM += toMB(c.Usage.Memory().Value())
|
||||
}
|
||||
mmx[p.Namespace+"/"+p.Name] = mx
|
||||
}
|
||||
}
|
||||
|
||||
// 0---------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
const megaByte = 1024 * 1024
|
||||
|
||||
// toMB converts bytes to megabytes.
|
||||
func toMB(v int64) float64 {
|
||||
return float64(v) / megaByte
|
||||
}
|
||||
|
||||
func toPerc(v1, v2 float64) float64 {
|
||||
if v2 == 0 {
|
||||
return 0
|
||||
}
|
||||
return math.Round((v1 / v2) * 100)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package k8s
|
||||
package client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
|
@ -7,6 +7,7 @@ import (
|
|||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
||||
v1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
||||
)
|
||||
|
||||
|
|
@ -52,9 +53,11 @@ func BenchmarkPodsMetrics(b *testing.B) {
|
|||
func TestNodesMetrics(t *testing.T) {
|
||||
m := NewMetricsServer(nil)
|
||||
|
||||
nodes := Collection{
|
||||
makeNode("n1", "32", "128Gi", "50m", "2Mi"),
|
||||
makeNode("n2", "8", "4Gi", "50m", "2Mi"),
|
||||
nodes := v1.NodeList{
|
||||
Items: []v1.Node{
|
||||
makeNode("n1", "32", "128Gi", "50m", "2Mi"),
|
||||
makeNode("n2", "8", "4Gi", "50m", "10Mi"),
|
||||
},
|
||||
}
|
||||
|
||||
metrics := v1beta1.NodeMetricsList{
|
||||
|
|
@ -65,7 +68,7 @@ func TestNodesMetrics(t *testing.T) {
|
|||
}
|
||||
|
||||
mmx := make(NodesMetrics)
|
||||
m.NodesMetrics(nodes, &metrics, mmx)
|
||||
m.NodesMetrics(&nodes, &metrics, mmx)
|
||||
assert.Equal(t, 2, len(mmx))
|
||||
mx, ok := mmx["n1"]
|
||||
assert.True(t, ok)
|
||||
|
|
@ -78,9 +81,11 @@ func TestNodesMetrics(t *testing.T) {
|
|||
}
|
||||
|
||||
func BenchmarkNodesMetrics(b *testing.B) {
|
||||
nodes := Collection{
|
||||
makeNode("n1", "100m", "4Mi", "50m", "2Mi"),
|
||||
makeNode("n2", "100m", "4Mi", "50m", "2Mi"),
|
||||
nodes := v1.NodeList{
|
||||
Items: []v1.Node{
|
||||
makeNode("n1", "100m", "4Mi", "100m", "2Mi"),
|
||||
makeNode("n2", "100m", "4Mi", "100m", "2Mi"),
|
||||
},
|
||||
}
|
||||
|
||||
metrics := v1beta1.NodeMetricsList{
|
||||
|
|
@ -96,38 +101,46 @@ func BenchmarkNodesMetrics(b *testing.B) {
|
|||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
m.NodesMetrics(nodes, &metrics, mmx)
|
||||
m.NodesMetrics(&nodes, &metrics, mmx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterLoad(t *testing.T) {
|
||||
m := NewMetricsServer(nil)
|
||||
|
||||
nodes := Collection{
|
||||
makeNode("n1", "100m", "4Mi", "50m", "2Mi"),
|
||||
makeNode("n2", "100m", "4Mi", "50m", "2Mi"),
|
||||
nodes := v1.NodeList{
|
||||
Items: []v1.Node{
|
||||
makeNode("n1", "100m", "4Mi", "50m", "2Mi"),
|
||||
makeNode("n2", "100m", "4Mi", "50m", "2Mi"),
|
||||
},
|
||||
}
|
||||
|
||||
metrics := Collection{
|
||||
makeMxNode("n1", "50m", "1Mi"),
|
||||
makeMxNode("n2", "50m", "1Mi"),
|
||||
metrics := mv1beta1.NodeMetricsList{
|
||||
Items: []mv1beta1.NodeMetrics{
|
||||
*makeMxNode("n1", "50m", "1Mi"),
|
||||
*makeMxNode("n2", "50m", "1Mi"),
|
||||
},
|
||||
}
|
||||
|
||||
var mx ClusterMetrics
|
||||
m.ClusterLoad(nodes, metrics, &mx)
|
||||
m.ClusterLoad(&nodes, &metrics, &mx)
|
||||
assert.Equal(t, 100.0, mx.PercCPU)
|
||||
assert.Equal(t, 50.0, mx.PercMEM)
|
||||
}
|
||||
|
||||
func BenchmarkClusterLoad(b *testing.B) {
|
||||
nodes := Collection{
|
||||
makeNode("n1", "100m", "4Mi", "50m", "2Mi"),
|
||||
makeNode("n2", "100m", "4Mi", "50m", "2Mi"),
|
||||
nodes := v1.NodeList{
|
||||
Items: []v1.Node{
|
||||
makeNode("n1", "100m", "4Mi", "50m", "2Mi"),
|
||||
makeNode("n2", "100m", "4Mi", "50m", "2Mi"),
|
||||
},
|
||||
}
|
||||
|
||||
metrics := Collection{
|
||||
makeMxNode("n1", "50m", "1Mi"),
|
||||
makeMxNode("n2", "50m", "1Mi"),
|
||||
metrics := mv1beta1.NodeMetricsList{
|
||||
Items: []mv1beta1.NodeMetrics{
|
||||
*makeMxNode("n1", "50m", "1Mi"),
|
||||
*makeMxNode("n2", "50m", "1Mi"),
|
||||
},
|
||||
}
|
||||
|
||||
m := NewMetricsServer(nil)
|
||||
|
|
@ -135,7 +148,7 @@ func BenchmarkClusterLoad(b *testing.B) {
|
|||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
m.ClusterLoad(nodes, metrics, &mx)
|
||||
m.ClusterLoad(&nodes, &metrics, &mx)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -156,8 +169,8 @@ func makeMxPod(name, cpu, mem string) *v1beta1.PodMetrics {
|
|||
}
|
||||
}
|
||||
|
||||
func makeNode(name, tcpu, tmem, acpu, amem string) *v1.Node {
|
||||
return &v1.Node{
|
||||
func makeNode(name, tcpu, tmem, acpu, amem string) v1.Node {
|
||||
return v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
|
|
@ -17,7 +17,8 @@ func TestColorize(t *testing.T) {
|
|||
"default": {"blee", 0, "\x1b[37mblee\x1b[0m"},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, Colorize(u.s, u.c))
|
||||
})
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ var K9sAlias = filepath.Join(K9sHome, "alias.yml")
|
|||
// Alias tracks shortname to GVR mappings.
|
||||
type Alias map[string]string
|
||||
|
||||
// ShortNames represents a collection of shortnames for aliases.
|
||||
type ShortNames map[string][]string
|
||||
|
||||
// Aliases represents a collection of aliases.
|
||||
type Aliases struct {
|
||||
Alias Alias `yaml:"alias"`
|
||||
|
|
@ -21,54 +24,64 @@ type Aliases struct {
|
|||
|
||||
// NewAliases return a new alias.
|
||||
func NewAliases() Aliases {
|
||||
aa := Aliases{Alias: make(Alias, 50)}
|
||||
aa.loadDefaults()
|
||||
return aa
|
||||
return Aliases{
|
||||
Alias: make(Alias, 50),
|
||||
}
|
||||
}
|
||||
|
||||
func (a Aliases) loadDefaults() {
|
||||
const (
|
||||
contexts = "contexts"
|
||||
portFwds = "portforwards"
|
||||
benchmarks = "benchmarks"
|
||||
dumps = "screendumps"
|
||||
groups = "groups"
|
||||
users = "users"
|
||||
)
|
||||
|
||||
a.Alias["dp"] = "apps/v1/deployments"
|
||||
a.Alias["sec"] = "v1/secrets"
|
||||
a.Alias["jo"] = "batch/v1/jobs"
|
||||
a.Alias["cr"] = "rbac.authorization.k8s.io/v1/clusterroles"
|
||||
a.Alias["crb"] = "rbac.authorization.k8s.io/v1/clusterrolebindings"
|
||||
a.Alias["ro"] = "rbac.authorization.k8s.io/v1/roles"
|
||||
a.Alias["rob"] = "rbac.authorization.k8s.io/v1/rolebindings"
|
||||
a.Alias["rb"] = "rbac.authorization.k8s.io/v1/rolebindings"
|
||||
a.Alias["np"] = "networking.k8s.io/v1/networkpolicies"
|
||||
{
|
||||
a.Alias["ctx"] = "contexts"
|
||||
a.Alias["contexts"] = "contexts"
|
||||
a.Alias["context"] = "contexts"
|
||||
a.Alias["ctx"] = contexts
|
||||
a.Alias[contexts] = contexts
|
||||
a.Alias["context"] = contexts
|
||||
}
|
||||
{
|
||||
a.Alias["usr"] = "users"
|
||||
a.Alias["users"] = "users"
|
||||
a.Alias["user"] = "user"
|
||||
a.Alias["usr"] = users
|
||||
a.Alias[users] = users
|
||||
a.Alias["user"] = users
|
||||
}
|
||||
{
|
||||
a.Alias["grp"] = "groups"
|
||||
a.Alias["group"] = "groups"
|
||||
a.Alias["groups"] = "groups"
|
||||
a.Alias["grp"] = groups
|
||||
a.Alias["group"] = groups
|
||||
a.Alias[groups] = groups
|
||||
}
|
||||
{
|
||||
a.Alias["pf"] = "portforwards"
|
||||
a.Alias["portforwards"] = "portforwards"
|
||||
a.Alias["portforward"] = "portforwards"
|
||||
a.Alias["pf"] = portFwds
|
||||
a.Alias[portFwds] = portFwds
|
||||
a.Alias["portforward"] = portFwds
|
||||
}
|
||||
{
|
||||
a.Alias["be"] = "benchmarks"
|
||||
a.Alias["benchmark"] = "benchmarks"
|
||||
a.Alias["benchmarks"] = "benchmarks"
|
||||
a.Alias["be"] = benchmarks
|
||||
a.Alias["benchmark"] = benchmarks
|
||||
a.Alias[benchmarks] = benchmarks
|
||||
}
|
||||
{
|
||||
a.Alias["sd"] = "screendumps"
|
||||
a.Alias["screendump"] = "screendumps"
|
||||
a.Alias["screendumps"] = "screendumps"
|
||||
a.Alias["sd"] = dumps
|
||||
a.Alias["screendump"] = dumps
|
||||
a.Alias[dumps] = dumps
|
||||
}
|
||||
}
|
||||
|
||||
// Load K9s aliases.
|
||||
func (a Aliases) Load() error {
|
||||
a.loadDefaults()
|
||||
return a.LoadAliases(K9sAlias)
|
||||
}
|
||||
|
||||
|
|
@ -79,20 +92,21 @@ func (a Aliases) Get(k string) (string, bool) {
|
|||
}
|
||||
|
||||
// Define declares a new alias.
|
||||
func (a Aliases) Define(command, alias string) {
|
||||
if _, ok := a.Alias[alias]; ok {
|
||||
// Don't override aliases. Take order of alias registration as precedence.
|
||||
return
|
||||
func (a Aliases) Define(gvr string, aliases ...string) {
|
||||
for _, alias := range aliases {
|
||||
if _, ok := a.Alias[alias]; ok {
|
||||
continue
|
||||
}
|
||||
a.Alias[alias] = gvr
|
||||
}
|
||||
|
||||
a.Alias[alias] = command
|
||||
}
|
||||
|
||||
// LoadAliases loads alias from a given file.
|
||||
func (a Aliases) LoadAliases(path string) error {
|
||||
f, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
log.Warn().Err(err).Msgf("No custom aliases found")
|
||||
return nil
|
||||
}
|
||||
|
||||
var aa Aliases
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ func TestAliasDefine(t *testing.T) {
|
|||
aliases []string
|
||||
}
|
||||
|
||||
tts := []struct {
|
||||
uu := []struct {
|
||||
name string
|
||||
aliases []aliasDef
|
||||
registeredCommands map[string]string
|
||||
|
|
@ -51,15 +51,16 @@ func TestAliasDefine(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for _, tt := range tts {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
for i := range uu {
|
||||
u := uu[i]
|
||||
t.Run(u.name, func(t *testing.T) {
|
||||
configAlias := config.NewAliases()
|
||||
for _, aliases := range tt.aliases {
|
||||
for _, aliases := range u.aliases {
|
||||
for _, a := range aliases.aliases {
|
||||
configAlias.Define(aliases.cmd, a)
|
||||
}
|
||||
}
|
||||
for alias, cmd := range tt.registeredCommands {
|
||||
for alias, cmd := range u.registeredCommands {
|
||||
v, ok := configAlias.Get(alias)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, cmd, v, "Wrong command for alias "+alias)
|
||||
|
|
@ -70,18 +71,17 @@ func TestAliasDefine(t *testing.T) {
|
|||
|
||||
func TestAliasesLoad(t *testing.T) {
|
||||
a := config.NewAliases()
|
||||
assert.Nil(t, a.LoadAliases("test_assets/alias.yml"))
|
||||
|
||||
assert.Equal(t, 27, len(a.Alias))
|
||||
assert.Nil(t, a.LoadAliases("test_assets/alias.yml"))
|
||||
assert.Equal(t, 2, len(a.Alias))
|
||||
}
|
||||
|
||||
func TestAliasesSave(t *testing.T) {
|
||||
a := config.NewAliases()
|
||||
|
||||
a.Alias["test"] = "fred"
|
||||
a.Alias["blee"] = "duh"
|
||||
a.SaveAliases("/tmp/a.yml")
|
||||
|
||||
assert.Nil(t, a.SaveAliases("/tmp/a.yml"))
|
||||
assert.Nil(t, a.LoadAliases("/tmp/a.yml"))
|
||||
assert.Equal(t, 28, len(a.Alias))
|
||||
assert.Equal(t, 2, len(a.Alias))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ func TestBenchEmpty(t *testing.T) {
|
|||
"notEmpty": {newBenchmark(), false},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, u.b.empty())
|
||||
})
|
||||
|
|
@ -46,7 +47,8 @@ func TestBenchLoad(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
b, err := NewBench(u.file)
|
||||
|
||||
|
|
@ -95,7 +97,8 @@ func TestBenchServiceLoad(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
b, err := NewBench("test_assets/b_good.yml")
|
||||
|
||||
|
|
@ -165,7 +168,8 @@ func TestBenchContainerLoad(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
b, err := NewBench("test_assets/b_containers.yml")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package config
|
||||
|
||||
import "github.com/derailed/k9s/internal/client"
|
||||
|
||||
// Cluster tracks K9s cluster configuration.
|
||||
type Cluster struct {
|
||||
Namespace *Namespace `yaml:"namespace"`
|
||||
|
|
@ -12,7 +14,7 @@ func NewCluster() *Cluster {
|
|||
}
|
||||
|
||||
// Validate a cluster config.
|
||||
func (c *Cluster) Validate(conn Connection, ks KubeSettings) {
|
||||
func (c *Cluster) Validate(conn client.Connection, ks KubeSettings) {
|
||||
if c.Namespace == nil {
|
||||
c.Namespace = NewNamespace()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
package config
|
||||
|
||||
// BOZO!! Once yaml is stable implement validation
|
||||
// go get gopkg.in/validator.v2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -10,8 +7,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gopkg.in/yaml.v2"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
|
@ -30,9 +26,6 @@ var (
|
|||
)
|
||||
|
||||
type (
|
||||
// Connection represents a kubernetes api server connection.
|
||||
Connection k8s.Connection
|
||||
|
||||
// KubeSettings exposes kubeconfig context information.
|
||||
KubeSettings interface {
|
||||
// CurrentContextName returns the name of the current context.
|
||||
|
|
@ -54,7 +47,7 @@ type (
|
|||
// Config tracks K9s configuration options.
|
||||
Config struct {
|
||||
K9s *K9s `yaml:"k9s"`
|
||||
client Connection
|
||||
client client.Connection
|
||||
settings KubeSettings
|
||||
}
|
||||
)
|
||||
|
|
@ -84,7 +77,9 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags) error {
|
|||
}
|
||||
c.K9s.CurrentCluster = ctx.Cluster
|
||||
if len(ctx.Namespace) != 0 {
|
||||
c.SetActiveNamespace(ctx.Namespace)
|
||||
if err := c.SetActiveNamespace(ctx.Namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if isSet(flags.ClusterName) {
|
||||
|
|
@ -92,7 +87,9 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags) error {
|
|||
}
|
||||
|
||||
if isSet(flags.Namespace) {
|
||||
c.SetActiveNamespace(*flags.Namespace)
|
||||
if err := c.SetActiveNamespace(*flags.Namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -119,7 +116,7 @@ func (c *Config) ActiveNamespace() string {
|
|||
return cl.Namespace.Active
|
||||
}
|
||||
}
|
||||
return resource.DefaultNamespace
|
||||
return "default"
|
||||
}
|
||||
|
||||
// FavNamespaces returns fav namespaces in the current cluster.
|
||||
|
|
@ -165,12 +162,12 @@ func (c *Config) SetActiveView(view string) {
|
|||
}
|
||||
|
||||
// GetConnection return an api server connection.
|
||||
func (c *Config) GetConnection() Connection {
|
||||
func (c *Config) GetConnection() client.Connection {
|
||||
return c.client
|
||||
}
|
||||
|
||||
// SetConnection set an api server connection.
|
||||
func (c *Config) SetConnection(conn Connection) {
|
||||
func (c *Config) SetConnection(conn client.Connection) {
|
||||
c.client = conn
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,8 @@ func TestConfigRefine(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
mc := NewMockConnection()
|
||||
m.When(mc.ValidNamespaces()).ThenReturn(namespaces(), nil)
|
||||
|
|
@ -142,7 +143,7 @@ func TestConfigSetActiveNamespace(t *testing.T) {
|
|||
cfg := config.NewConfig(mk)
|
||||
|
||||
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
|
||||
cfg.SetActiveNamespace("default")
|
||||
assert.Nil(t, cfg.SetActiveNamespace("default"))
|
||||
assert.Equal(t, "default", cfg.ActiveNamespace())
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +203,7 @@ func TestConfigSaveFile(t *testing.T) {
|
|||
|
||||
cfg := config.NewConfig(mk)
|
||||
cfg.SetConnection(mc)
|
||||
cfg.Load("test_assets/k9s.yml")
|
||||
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
|
||||
cfg.K9s.RefreshRate = 100
|
||||
cfg.K9s.LogBufferSize = 500
|
||||
cfg.K9s.LogRequestSize = 100
|
||||
|
|
@ -231,7 +232,7 @@ func TestConfigReset(t *testing.T) {
|
|||
|
||||
cfg := config.NewConfig(mk)
|
||||
cfg.SetConnection(mc)
|
||||
cfg.Load("test_assets/k9s.yml")
|
||||
assert.Nil(t, cfg.Load("test_assets/k9s.yml"))
|
||||
cfg.Reset()
|
||||
cfg.Validate()
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ func InNSList(nn []interface{}, ns string) bool {
|
|||
func mustK9sHome() string {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal().Err(err).Msg("Die on retriving user home")
|
||||
}
|
||||
return usr.HomeDir
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ func mustK9sHome() string {
|
|||
func MustK9sUser() string {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal().Err(err).Msg("Die on retriving user info")
|
||||
}
|
||||
return usr.Username
|
||||
}
|
||||
|
|
@ -59,8 +59,7 @@ func EnsurePath(path string, mod os.FileMode) {
|
|||
dir := filepath.Dir(path)
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, mod); err != nil {
|
||||
log.Error().Msgf("Unable to create K9s home config dir: %v", err)
|
||||
panic(err)
|
||||
log.Fatal().Msgf("Unable to create K9s home config dir: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// K9sHotKeys manages K9s hotKeys.
|
||||
var K9sHotKeys = filepath.Join(K9sHome, "hotkey.yml")
|
||||
|
||||
// HotKeys represents a collection of plugins.
|
||||
type HotKeys struct {
|
||||
HotKey map[string]HotKey `yaml:"hotKey"`
|
||||
}
|
||||
|
||||
// HotKey describes a K9s hotkey.
|
||||
type HotKey struct {
|
||||
ShortCut string `yaml:"shortCut"`
|
||||
Description string `yaml:"description"`
|
||||
Command string `yaml:"command"`
|
||||
}
|
||||
|
||||
// NewHotKeys returns a new plugin.
|
||||
func NewHotKeys() HotKeys {
|
||||
return HotKeys{
|
||||
HotKey: make(map[string]HotKey),
|
||||
}
|
||||
}
|
||||
|
||||
// Load K9s plugins.
|
||||
func (h HotKeys) Load() error {
|
||||
return h.LoadHotKeys(K9sHotKeys)
|
||||
}
|
||||
|
||||
// LoadHotKeys loads plugins from a given file.
|
||||
func (h HotKeys) LoadHotKeys(path string) error {
|
||||
f, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var hh HotKeys
|
||||
if err := yaml.Unmarshal(f, &hh); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range hh.HotKey {
|
||||
h.HotKey[k] = v
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package config_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestHotKeyLoad(t *testing.T) {
|
||||
h := config.NewHotKeys()
|
||||
assert.Nil(t, h.LoadHotKeys("test_assets/hot_key.yml"))
|
||||
|
||||
assert.Equal(t, 1, len(h.HotKey))
|
||||
|
||||
k, ok := h.HotKey["pods"]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "shift-0", k.ShortCut)
|
||||
assert.Equal(t, "Launch pod view", k.Description)
|
||||
assert.Equal(t, "pods", k.Command)
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
)
|
||||
import "github.com/derailed/k9s/internal/client"
|
||||
|
||||
const (
|
||||
defaultRefreshRate = 2
|
||||
|
|
@ -114,7 +112,7 @@ func (k *K9s) checkClusters(ks KubeSettings) {
|
|||
}
|
||||
|
||||
// Validate the current configuration.
|
||||
func (k *K9s) Validate(c k8s.Connection, ks KubeSettings) {
|
||||
func (k *K9s) Validate(c client.Connection, ks KubeSettings) {
|
||||
k.validateDefaults()
|
||||
|
||||
if k.Clusters == nil {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// Code generated by pegomock. DO NOT EDIT.
|
||||
// Source: github.com/derailed/k9s/internal/config (interfaces: Connection)
|
||||
// Source: github.com/derailed/k9s/internal/client (interfaces: Connection)
|
||||
|
||||
package config_test
|
||||
|
||||
import (
|
||||
k8s "github.com/derailed/k9s/internal/k8s"
|
||||
client "github.com/derailed/k9s/internal/client"
|
||||
pegomock "github.com/petergtz/pegomock"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
version "k8s.io/apimachinery/pkg/version"
|
||||
|
|
@ -51,12 +51,12 @@ func (mock *MockConnection) CachedDiscovery() (*disk.CachedDiscoveryClient, erro
|
|||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (mock *MockConnection) CanIAccess(_param0 string, _param1 string, _param2 []string) (bool, error) {
|
||||
func (mock *MockConnection) CanI(_param0 string, _param1 string, _param2 []string) (bool, error) {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockConnection().")
|
||||
}
|
||||
params := []pegomock.Param{_param0, _param1, _param2}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("CanIAccess", params, []reflect.Type{reflect.TypeOf((*bool)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("CanI", params, []reflect.Type{reflect.TypeOf((*bool)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 bool
|
||||
var ret1 error
|
||||
if len(result) != 0 {
|
||||
|
|
@ -70,46 +70,16 @@ func (mock *MockConnection) CanIAccess(_param0 string, _param1 string, _param2 [
|
|||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (mock *MockConnection) CheckListNSAccess() error {
|
||||
func (mock *MockConnection) Config() *client.Config {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockConnection().")
|
||||
}
|
||||
params := []pegomock.Param{}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("CheckListNSAccess", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 error
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("Config", params, []reflect.Type{reflect.TypeOf((**client.Config)(nil)).Elem()})
|
||||
var ret0 *client.Config
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(error)
|
||||
}
|
||||
}
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (mock *MockConnection) CheckNSAccess(_param0 string) error {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockConnection().")
|
||||
}
|
||||
params := []pegomock.Param{_param0}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("CheckNSAccess", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(error)
|
||||
}
|
||||
}
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (mock *MockConnection) Config() *k8s.Config {
|
||||
if mock == nil {
|
||||
panic("mock must not be nil. Use myMock := NewMockConnection().")
|
||||
}
|
||||
params := []pegomock.Param{}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("Config", params, []reflect.Type{reflect.TypeOf((**k8s.Config)(nil)).Elem()})
|
||||
var ret0 *k8s.Config
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(*k8s.Config)
|
||||
ret0 = result[0].(*client.Config)
|
||||
}
|
||||
}
|
||||
return ret0
|
||||
|
|
@ -419,23 +389,23 @@ func (c *MockConnection_CachedDiscovery_OngoingVerification) GetCapturedArgument
|
|||
func (c *MockConnection_CachedDiscovery_OngoingVerification) GetAllCapturedArguments() {
|
||||
}
|
||||
|
||||
func (verifier *VerifierMockConnection) CanIAccess(_param0 string, _param1 string, _param2 []string) *MockConnection_CanIAccess_OngoingVerification {
|
||||
func (verifier *VerifierMockConnection) CanI(_param0 string, _param1 string, _param2 []string) *MockConnection_CanI_OngoingVerification {
|
||||
params := []pegomock.Param{_param0, _param1, _param2}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "CanIAccess", params, verifier.timeout)
|
||||
return &MockConnection_CanIAccess_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "CanI", params, verifier.timeout)
|
||||
return &MockConnection_CanI_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type MockConnection_CanIAccess_OngoingVerification struct {
|
||||
type MockConnection_CanI_OngoingVerification struct {
|
||||
mock *MockConnection
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *MockConnection_CanIAccess_OngoingVerification) GetCapturedArguments() (string, string, []string) {
|
||||
func (c *MockConnection_CanI_OngoingVerification) GetCapturedArguments() (string, string, []string) {
|
||||
_param0, _param1, _param2 := c.GetAllCapturedArguments()
|
||||
return _param0[len(_param0)-1], _param1[len(_param1)-1], _param2[len(_param2)-1]
|
||||
}
|
||||
|
||||
func (c *MockConnection_CanIAccess_OngoingVerification) GetAllCapturedArguments() (_param0 []string, _param1 []string, _param2 [][]string) {
|
||||
func (c *MockConnection_CanI_OngoingVerification) GetAllCapturedArguments() (_param0 []string, _param1 []string, _param2 [][]string) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]string, len(params[0]))
|
||||
|
|
@ -454,50 +424,6 @@ func (c *MockConnection_CanIAccess_OngoingVerification) GetAllCapturedArguments(
|
|||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierMockConnection) CheckListNSAccess() *MockConnection_CheckListNSAccess_OngoingVerification {
|
||||
params := []pegomock.Param{}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "CheckListNSAccess", params, verifier.timeout)
|
||||
return &MockConnection_CheckListNSAccess_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type MockConnection_CheckListNSAccess_OngoingVerification struct {
|
||||
mock *MockConnection
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *MockConnection_CheckListNSAccess_OngoingVerification) GetCapturedArguments() {
|
||||
}
|
||||
|
||||
func (c *MockConnection_CheckListNSAccess_OngoingVerification) GetAllCapturedArguments() {
|
||||
}
|
||||
|
||||
func (verifier *VerifierMockConnection) CheckNSAccess(_param0 string) *MockConnection_CheckNSAccess_OngoingVerification {
|
||||
params := []pegomock.Param{_param0}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "CheckNSAccess", params, verifier.timeout)
|
||||
return &MockConnection_CheckNSAccess_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type MockConnection_CheckNSAccess_OngoingVerification struct {
|
||||
mock *MockConnection
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *MockConnection_CheckNSAccess_OngoingVerification) GetCapturedArguments() string {
|
||||
_param0 := c.GetAllCapturedArguments()
|
||||
return _param0[len(_param0)-1]
|
||||
}
|
||||
|
||||
func (c *MockConnection_CheckNSAccess_OngoingVerification) GetAllCapturedArguments() (_param0 []string) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]string, len(params[0]))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(string)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierMockConnection) Config() *MockConnection_Config_OngoingVerification {
|
||||
params := []pegomock.Param{}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Config", params, verifier.timeout)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxFavoritesNS number # favorite namespaces to keep in the configuration.
|
||||
MaxFavoritesNS = 10
|
||||
MaxFavoritesNS = 9
|
||||
defaultNS = "default"
|
||||
allNS = "all"
|
||||
)
|
||||
|
|
@ -26,7 +27,7 @@ func NewNamespace() *Namespace {
|
|||
}
|
||||
|
||||
// Validate a namespace is setup correctly
|
||||
func (n *Namespace) Validate(c Connection, ks KubeSettings) {
|
||||
func (n *Namespace) Validate(c client.Connection, ks KubeSettings) {
|
||||
nns, err := c.ValidNamespaces()
|
||||
if err != nil {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -12,4 +12,11 @@ func TestPluginLoad(t *testing.T) {
|
|||
assert.Nil(t, p.LoadPlugins("test_assets/plugin.yml"))
|
||||
|
||||
assert.Equal(t, 1, len(p.Plugin))
|
||||
k, ok := p.Plugin["blah"]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "shift-s", k.ShortCut)
|
||||
assert.Equal(t, "blee", k.Description)
|
||||
assert.Equal(t, []string{"po", "dp"}, k.Scopes)
|
||||
assert.Equal(t, "duh", k.Command)
|
||||
assert.Equal(t, []string{"-n", "$NAMESPACE", "-boolean"}, k.Args)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,15 @@ var (
|
|||
K9sStylesFile = filepath.Join(K9sHome, "skin.yml")
|
||||
)
|
||||
|
||||
type StyleListener interface {
|
||||
StylesChanged(*Styles)
|
||||
}
|
||||
|
||||
type (
|
||||
// Styles tracks K9s styling options.
|
||||
Styles struct {
|
||||
K9s Style `yaml:"k9s"`
|
||||
K9s Style `yaml:"k9s"`
|
||||
listeners []StyleListener
|
||||
}
|
||||
|
||||
// Body tracks body styles.
|
||||
|
|
@ -132,7 +137,7 @@ func newStyle() Style {
|
|||
Body: newBody(),
|
||||
Frame: newFrame(),
|
||||
Info: newInfo(),
|
||||
Table: newTable(),
|
||||
Table: newGetTable(),
|
||||
Views: newViews(),
|
||||
}
|
||||
}
|
||||
|
|
@ -211,12 +216,12 @@ func newInfo() Info {
|
|||
}
|
||||
|
||||
// NewTable returns a new table style.
|
||||
func newTable() Table {
|
||||
func newGetTable() Table {
|
||||
return Table{
|
||||
FgColor: "aqua",
|
||||
BgColor: "black",
|
||||
CursorColor: "aqua",
|
||||
MarkColor: "darkgoldenrod",
|
||||
MarkColor: "violet",
|
||||
Header: newTableHeader(),
|
||||
}
|
||||
}
|
||||
|
|
@ -257,9 +262,10 @@ func newMenu() Menu {
|
|||
}
|
||||
|
||||
// NewStyles creates a new default config.
|
||||
func NewStyles(path string) (*Styles, error) {
|
||||
s := &Styles{K9s: newStyle()}
|
||||
return s, s.load(path)
|
||||
func NewStyles() *Styles {
|
||||
return &Styles{
|
||||
K9s: newStyle(),
|
||||
}
|
||||
}
|
||||
|
||||
// FgColor returns the foreground color.
|
||||
|
|
@ -272,6 +278,30 @@ func (s *Styles) BgColor() tcell.Color {
|
|||
return AsColor(s.Body().BgColor)
|
||||
}
|
||||
|
||||
func (s *Styles) AddListener(l StyleListener) {
|
||||
s.listeners = append(s.listeners, l)
|
||||
}
|
||||
|
||||
func (s *Styles) RemoveListener(l StyleListener) {
|
||||
victim := -1
|
||||
for i, lis := range s.listeners {
|
||||
if lis == l {
|
||||
victim = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if victim == -1 {
|
||||
return
|
||||
}
|
||||
s.listeners = append(s.listeners[:victim], s.listeners[victim+1:]...)
|
||||
}
|
||||
|
||||
func (s *Styles) fireStylesChanged() {
|
||||
for _, list := range s.listeners {
|
||||
list.StylesChanged(s)
|
||||
}
|
||||
}
|
||||
|
||||
// Body returns body styles.
|
||||
func (s *Styles) Body() Body {
|
||||
return s.K9s.Body
|
||||
|
|
@ -293,7 +323,7 @@ func (s *Styles) Title() Title {
|
|||
}
|
||||
|
||||
// Table returns table styles.
|
||||
func (s *Styles) Table() Table {
|
||||
func (s *Styles) GetTable() Table {
|
||||
return s.K9s.Table
|
||||
}
|
||||
|
||||
|
|
@ -303,7 +333,7 @@ func (s *Styles) Views() Views {
|
|||
}
|
||||
|
||||
// Load K9s configuration from file
|
||||
func (s *Styles) load(path string) error {
|
||||
func (s *Styles) Load(path string) error {
|
||||
f, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -312,6 +342,7 @@ func (s *Styles) load(path string) error {
|
|||
if err := yaml.Unmarshal(f, s); err != nil {
|
||||
return err
|
||||
}
|
||||
s.fireStylesChanged()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -327,10 +358,9 @@ func (s *Styles) Update() {
|
|||
|
||||
// AsColor checks color index, if match return color otherwise pink it is.
|
||||
func AsColor(c string) tcell.Color {
|
||||
// Use tcell.GetColor to support hex codes.
|
||||
// "Creates a Color from a color name (W3C name). A hex value may be supplied as a string in the format "#ffffff"."
|
||||
if color := tcell.GetColor(c); color != -1 {
|
||||
if color, ok := tcell.ColorNames[c]; ok {
|
||||
return color
|
||||
}
|
||||
return tcell.ColorPink
|
||||
|
||||
return tcell.GetColor(c)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +1,62 @@
|
|||
package config
|
||||
package config_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSkinNone(t *testing.T) {
|
||||
s, err := NewStyles("test_assets/empty_skin.yml")
|
||||
assert.Nil(t, err)
|
||||
func TestAsColor(t *testing.T) {
|
||||
uu := map[string]tcell.Color{
|
||||
"blah": tcell.ColorDefault,
|
||||
"blue": tcell.ColorBlue,
|
||||
"#ffffff": tcell.NewHexColor(33554431),
|
||||
"#ff0000": tcell.NewHexColor(33488896),
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
c, u := k, uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u, config.AsColor(c))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSkinNone(t *testing.T) {
|
||||
s := config.NewStyles()
|
||||
assert.Nil(t, s.Load("test_assets/empty_skin.yml"))
|
||||
s.Update()
|
||||
|
||||
assert.Equal(t, "cadetblue", s.Body().FgColor)
|
||||
assert.Equal(t, "black", s.Body().BgColor)
|
||||
assert.Equal(t, "black", s.Table().BgColor)
|
||||
assert.Equal(t, "black", s.GetTable().BgColor)
|
||||
assert.Equal(t, tcell.ColorCadetBlue, s.FgColor())
|
||||
assert.Equal(t, tcell.ColorBlack, s.BgColor())
|
||||
assert.Equal(t, tcell.ColorBlack, tview.Styles.PrimitiveBackgroundColor)
|
||||
assert.Equal(t, tcell.ColorPink, AsColor("blah"))
|
||||
assert.Equal(t, tcell.ColorWhite, AsColor("white"))
|
||||
}
|
||||
|
||||
func TestSkin(t *testing.T) {
|
||||
s, err := NewStyles("test_assets/black_and_wtf.yml")
|
||||
assert.Nil(t, err)
|
||||
|
||||
s := config.NewStyles()
|
||||
assert.Nil(t, s.Load("test_assets/black_and_wtf.yml"))
|
||||
s.Update()
|
||||
|
||||
assert.Equal(t, "white", s.Body().FgColor)
|
||||
assert.Equal(t, "black", s.Body().BgColor)
|
||||
assert.Equal(t, "black", s.Table().BgColor)
|
||||
assert.Equal(t, "black", s.GetTable().BgColor)
|
||||
assert.Equal(t, tcell.ColorWhite, s.FgColor())
|
||||
assert.Equal(t, tcell.ColorBlack, s.BgColor())
|
||||
assert.Equal(t, tcell.ColorBlack, tview.Styles.PrimitiveBackgroundColor)
|
||||
assert.Equal(t, tcell.ColorPink, AsColor("blah"))
|
||||
assert.Equal(t, tcell.ColorWhite, AsColor("white"))
|
||||
}
|
||||
|
||||
func TestSkinNotExits(t *testing.T) {
|
||||
_, err := NewStyles("test_assets/blee.yml")
|
||||
assert.NotNil(t, err)
|
||||
s := config.NewStyles()
|
||||
assert.NotNil(t, s.Load("test_assets/blee.yml"))
|
||||
}
|
||||
|
||||
func TestSkinBoarked(t *testing.T) {
|
||||
_, err := NewStyles("test_assets/skin_boarked.yml")
|
||||
assert.NotNil(t, err)
|
||||
s := config.NewStyles()
|
||||
assert.NotNil(t, s.Load("test_assets/skin_boarked.yml"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
benchmarks:
|
||||
defaults:
|
||||
concurrency: 2
|
||||
requests: 1000
|
||||
services:
|
||||
default/nginx:
|
||||
concurrency: 2
|
||||
requests: 1000
|
||||
http:
|
||||
method: GET
|
||||
http2: true
|
||||
host: 10.10.10.10
|
||||
path: /
|
||||
body: |-
|
||||
{"fred": "blee"}
|
||||
headers:
|
||||
Accept:
|
||||
- text/html
|
||||
Content-Type:
|
||||
- application/json
|
||||
auth:
|
||||
user: "fred"
|
||||
password: "blee"
|
||||
blee/fred:
|
||||
concurrency: 10
|
||||
requests: 1500
|
||||
http:
|
||||
method: POST
|
||||
http2: false
|
||||
host: 20.20.20.20
|
||||
path: /zorg
|
||||
body: |-
|
||||
{"fred": "blee"}
|
||||
headers:
|
||||
Accept:
|
||||
- text/html
|
||||
Content-Type:
|
||||
- application/json
|
||||
auth:
|
||||
user: "fred"
|
||||
password: "blee"
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
hotKey:
|
||||
pods:
|
||||
shortCut: shift-0
|
||||
description: Launch pod view
|
||||
command: pods
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
)
|
||||
|
||||
// Alias tracks standard and custom command aliases.
|
||||
type Alias struct {
|
||||
config.Aliases
|
||||
factory Factory
|
||||
}
|
||||
|
||||
// NewAlias returns a new set of aliases.
|
||||
func NewAlias(f Factory) *Alias {
|
||||
return &Alias{
|
||||
Aliases: config.NewAliases(),
|
||||
factory: f,
|
||||
}
|
||||
}
|
||||
|
||||
// ClearAliases remove all aliases.
|
||||
func (a *Alias) Clear() {
|
||||
for k := range a.Alias {
|
||||
delete(a.Alias, k)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure makes sure alias are loaded.
|
||||
func (a *Alias) Ensure() (config.Alias, error) {
|
||||
if len(a.Alias) == 0 {
|
||||
if err := LoadResources(a.factory); err != nil {
|
||||
return config.Alias{}, err
|
||||
}
|
||||
return a.Alias, a.load()
|
||||
}
|
||||
return a.Alias, nil
|
||||
}
|
||||
|
||||
func (a *Alias) load() error {
|
||||
if err := a.Load(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, gvr := range AllGVRs() {
|
||||
meta, err := MetaFor(gvr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := a.Alias[meta.Kind]; ok {
|
||||
continue
|
||||
}
|
||||
a.Define(string(gvr), strings.ToLower(meta.Kind), meta.Name)
|
||||
if meta.SingularName != "" {
|
||||
a.Define(string(gvr), meta.SingularName)
|
||||
}
|
||||
if meta.ShortNames != nil {
|
||||
a.Define(string(gvr), meta.ShortNames...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
apiVersion: v1
|
||||
kind: Config
|
||||
preferences: {}
|
||||
clusters:
|
||||
- cluster:
|
||||
insecure-skip-tls-verify: true
|
||||
server: https://localhost:3000
|
||||
name: fred
|
||||
- cluster:
|
||||
insecure-skip-tls-verify: true
|
||||
server: https://localhost:3001
|
||||
name: blee
|
||||
- cluster:
|
||||
insecure-skip-tls-verify: true
|
||||
server: https://localhost:3002
|
||||
name: duh
|
||||
contexts:
|
||||
- context:
|
||||
cluster: fred
|
||||
user: fred
|
||||
name: fred
|
||||
- context:
|
||||
cluster: blee
|
||||
user: blee
|
||||
name: blee
|
||||
- context:
|
||||
cluster: duh
|
||||
user: duh
|
||||
name: duh
|
||||
current-context: fred
|
||||
users:
|
||||
- name: fred
|
||||
user:
|
||||
client-certificate-data: ZnJlZA==
|
||||
client-key-data: ZnJlZA==
|
||||
- name: blee
|
||||
user:
|
||||
client-certificate-data: ZnJlZA==
|
||||
client-key-data: ZnJlZA==
|
||||
- name: duh
|
||||
user:
|
||||
client-certificate-data: ZnJlZA==
|
||||
client-key-data: ZnJlZA==
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
insecure-skip-tls-verify: true
|
||||
server: https://localhost:3001
|
||||
name: blee
|
||||
- cluster:
|
||||
insecure-skip-tls-verify: true
|
||||
server: https://localhost:3002
|
||||
name: duh
|
||||
- cluster:
|
||||
insecure-skip-tls-verify: true
|
||||
server: https://localhost:3000
|
||||
name: fred
|
||||
contexts:
|
||||
- context:
|
||||
cluster: blee
|
||||
user: blee
|
||||
name: blee
|
||||
- context:
|
||||
cluster: duh
|
||||
user: duh
|
||||
name: duh
|
||||
current-context: fred
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: blee
|
||||
user:
|
||||
client-certificate-data: ZnJlZA==
|
||||
client-key-data: ZnJlZA==
|
||||
- name: duh
|
||||
user:
|
||||
client-certificate-data: ZnJlZA==
|
||||
client-key-data: ZnJlZA==
|
||||
- name: fred
|
||||
user:
|
||||
client-certificate-data: ZnJlZA==
|
||||
client-key-data: ZnJlZA==
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Benchmark represents a benchmark resource.
|
||||
type Benchmark struct {
|
||||
Generic
|
||||
}
|
||||
|
||||
var _ Accessor = &Benchmark{}
|
||||
var _ Nuker = &Benchmark{}
|
||||
|
||||
// Delete a Benchmark.
|
||||
func (d *Benchmark) Delete(path string, cascade, force bool) error {
|
||||
return os.Remove(path)
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/watch"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
type Container struct {
|
||||
Generic
|
||||
}
|
||||
|
||||
var _ Accessor = &Container{}
|
||||
var _ Loggable = &Container{}
|
||||
|
||||
// Logs tails a given container logs
|
||||
func (c *Container) TailLogs(ctx context.Context, logChan chan<- string, opts LogOptions) error {
|
||||
fac, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)
|
||||
if !ok {
|
||||
return errors.New("Expecting an informer")
|
||||
}
|
||||
o, err := fac.Get("v1/pods", opts.Path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var po v1.Pod
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tailLogs(ctx, c, logChan, opts)
|
||||
}
|
||||
|
||||
// Logs fetch container logs for a given pod and container.
|
||||
func (c *Container) Logs(path string, opts *v1.PodLogOptions) *restclient.Request {
|
||||
ns, n := client.Namespaced(path)
|
||||
return c.Client().DialOrDie().CoreV1().Pods(ns).GetLogs(n, opts)
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/rs/zerolog/log"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
Generic
|
||||
}
|
||||
|
||||
var _ Accessor = &Context{}
|
||||
var _ Switchable = &Context{}
|
||||
|
||||
func (c *Context) config() *client.Config {
|
||||
return c.Factory.Client().Config()
|
||||
}
|
||||
|
||||
// Get a Context.
|
||||
func (c *Context) Get(_, n string) (runtime.Object, error) {
|
||||
ctx, err := c.config().GetContext(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &render.NamedContext{Name: n, Context: ctx}, nil
|
||||
}
|
||||
|
||||
// List all Contexts on the current cluster.
|
||||
func (c *Context) List(string, metav1.ListOptions) ([]runtime.Object, error) {
|
||||
ctxs, err := c.config().Contexts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make([]runtime.Object, 0, len(ctxs))
|
||||
for k, v := range ctxs {
|
||||
cc = append(cc, render.NewNamedContext(c.config(), k, v))
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a Context.
|
||||
func (c *Context) Delete(path string, cascade, force bool) error {
|
||||
ctx, err := c.config().CurrentContextName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx == path {
|
||||
return fmt.Errorf("trying to delete your current context %s", path)
|
||||
}
|
||||
|
||||
return c.config().DelContext(path)
|
||||
}
|
||||
|
||||
// MustCurrentContextName return the active context name.
|
||||
func (c *Context) MustCurrentContextName() string {
|
||||
cl, err := c.config().CurrentContextName()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Fetching current context")
|
||||
}
|
||||
return cl
|
||||
}
|
||||
|
||||
// Switch to another context.
|
||||
func (c *Context) Switch(ctx string) error {
|
||||
c.Factory.Client().SwitchContextOrDie(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
// KubeUpdate modifies kubeconfig default context.
|
||||
func (c *Context) KubeUpdate(n string) error {
|
||||
config, err := c.config().RawConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Switch(n); err != nil {
|
||||
return err
|
||||
}
|
||||
return clientcmd.ModifyConfig(
|
||||
clientcmd.NewDefaultPathOptions(), config, true,
|
||||
)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// // NamedContext represents a named cluster context.
|
||||
// type NamedContext struct {
|
||||
// Name string
|
||||
// Context *api.Context
|
||||
// config *client.Config
|
||||
// }
|
||||
|
||||
// // NewNamedContext returns a new named context.
|
||||
// func NewNamedContext(c *client.Config, n string, ctx *api.Context) *NamedContext {
|
||||
// return &NamedContext{Name: n, Context: ctx, config: c}
|
||||
// }
|
||||
|
||||
// // MustCurrentContextName return the active context name.
|
||||
// func (c *NamedContext) MustCurrentContextName() string {
|
||||
// cl, err := c.config.CurrentContextName()
|
||||
// if err != nil {
|
||||
// log.Fatal().Err(err).Msg("Fetching current context")
|
||||
// }
|
||||
// return cl
|
||||
// }
|
||||
|
||||
// // GetObjectKind returns a schema object.
|
||||
// func (c *NamedContext) GetObjectKind() schema.ObjectKind {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // DeepCopyObject returns a container copy.
|
||||
// func (c *NamedContext) DeepCopyObject() runtime.Object {
|
||||
// return c
|
||||
// }
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
)
|
||||
|
||||
const maxJobNameSize = 42
|
||||
|
||||
type CronJob struct {
|
||||
Generic
|
||||
}
|
||||
|
||||
var _ Accessor = &CronJob{}
|
||||
var _ Runnable = &CronJob{}
|
||||
|
||||
// Run a CronJob.
|
||||
func (c *CronJob) Run(path string) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
cj, err := c.Client().DialOrDie().BatchV1beta1().CronJobs(ns).Get(n, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var jobName = cj.Name
|
||||
if len(cj.Name) >= maxJobNameSize {
|
||||
jobName = cj.Name[0:maxJobNameSize]
|
||||
}
|
||||
|
||||
job := &batchv1.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: jobName + "-manual-" + rand.String(3),
|
||||
Namespace: ns,
|
||||
Labels: cj.Spec.JobTemplate.Labels,
|
||||
},
|
||||
Spec: cj.Spec.JobTemplate.Spec,
|
||||
}
|
||||
_, err = c.Client().DialOrDie().BatchV1().Jobs(ns).Create(job)
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
"k8s.io/kubectl/pkg/describe"
|
||||
"k8s.io/kubectl/pkg/describe/versioned"
|
||||
)
|
||||
|
||||
func Describe(c client.Connection, gvr client.GVR, ns, n string) (string, error) {
|
||||
mapper := RestMapper{Connection: c}
|
||||
m, err := mapper.ToRESTMapper()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("No REST mapper for resource %s", gvr)
|
||||
return "", err
|
||||
}
|
||||
|
||||
GVR := client.GVR(gvr)
|
||||
gvk, err := m.KindFor(GVR.AsGVR())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("No GVK for resource %s", gvr)
|
||||
return "", err
|
||||
}
|
||||
|
||||
mapping, err := mapper.ResourceFor(GVR.ResName(), gvk.Kind)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Unable to find mapper for %s %s", gvr, n)
|
||||
return "", err
|
||||
}
|
||||
d, err := versioned.Describer(c.Config().Flags(), mapping)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Unable to find describer for %#v", mapping)
|
||||
return "", err
|
||||
}
|
||||
|
||||
log.Debug().Msgf("DESCRIBE FOR %q -- %q:%q", gvr, ns, n)
|
||||
return d.Describe(ns, n, describe.DescriberSettings{ShowEvents: true})
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubectl/pkg/polymorphichelpers"
|
||||
)
|
||||
|
||||
type Deployment struct {
|
||||
Generic
|
||||
}
|
||||
|
||||
var _ Accessor = &Deployment{}
|
||||
var _ Loggable = &Deployment{}
|
||||
var _ Restartable = &Deployment{}
|
||||
var _ Scalable = &Deployment{}
|
||||
|
||||
// Scale a Deployment.
|
||||
func (d *Deployment) Scale(path string, replicas int32) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
scale, err := d.Client().DialOrDie().AppsV1().Deployments(ns).GetScale(n, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scale.Spec.Replicas = replicas
|
||||
_, err = d.Client().DialOrDie().AppsV1().Deployments(ns).UpdateScale(n, scale)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Restart a Deployment rollout.
|
||||
func (d *Deployment) Restart(path string) error {
|
||||
o, err := d.Get(string(d.gvr), path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ds appsv1.Deployment
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
update, err := polymorphichelpers.ObjectRestarterFn(&ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = d.Client().DialOrDie().AppsV1().Deployments(ds.Namespace).Patch(ds.Name, types.StrategicMergePatchType, update)
|
||||
return err
|
||||
}
|
||||
|
||||
// Logs tail logs for all pods represented by this Deployment.
|
||||
func (d *Deployment) TailLogs(ctx context.Context, c chan<- string, opts LogOptions) error {
|
||||
o, err := d.Get(string(d.gvr), opts.Path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var dp appsv1.Deployment
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &dp)
|
||||
if err != nil {
|
||||
return errors.New("expecting Deployment resource")
|
||||
}
|
||||
|
||||
if dp.Spec.Selector == nil || len(dp.Spec.Selector.MatchLabels) == 0 {
|
||||
return fmt.Errorf("No valid selector found on Deployment %s", opts.Path)
|
||||
}
|
||||
|
||||
return podLogs(ctx, c, dp.Spec.Selector.MatchLabels, opts)
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/watch"
|
||||
"github.com/rs/zerolog/log"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubectl/pkg/polymorphichelpers"
|
||||
)
|
||||
|
||||
type DaemonSet struct {
|
||||
Generic
|
||||
}
|
||||
|
||||
var _ Accessor = &DaemonSet{}
|
||||
var _ Loggable = &DaemonSet{}
|
||||
var _ Restartable = &DaemonSet{}
|
||||
|
||||
// Restart a DaemonSet rollout.
|
||||
func (d *DaemonSet) Restart(path string) error {
|
||||
o, err := d.Get(string(d.gvr), path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ds appsv1.DaemonSet
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
update, err := polymorphichelpers.ObjectRestarterFn(&ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = d.Client().DialOrDie().AppsV1().DaemonSets(ds.Namespace).Patch(ds.Name, types.StrategicMergePatchType, update)
|
||||
return err
|
||||
}
|
||||
|
||||
// Logs tail logs for all pods represented by this DaemonSet.
|
||||
func (d *DaemonSet) TailLogs(ctx context.Context, c chan<- string, opts LogOptions) error {
|
||||
o, err := d.Get("apps/v1/daemonsets", opts.Path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ds appsv1.DaemonSet
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &ds)
|
||||
if err != nil {
|
||||
return errors.New("expecting daemonset resource")
|
||||
}
|
||||
|
||||
if ds.Spec.Selector == nil || len(ds.Spec.Selector.MatchLabels) == 0 {
|
||||
return fmt.Errorf("no valid selector found on daemonset %q", opts.Path)
|
||||
}
|
||||
|
||||
return podLogs(ctx, c, ds.Spec.Selector.MatchLabels, opts)
|
||||
}
|
||||
|
||||
func podLogs(ctx context.Context, c chan<- string, sel map[string]string, opts LogOptions) error {
|
||||
f, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)
|
||||
if !ok {
|
||||
return errors.New("expecting a context factory")
|
||||
}
|
||||
ls, err := metav1.ParseToLabelSelector(toSelector(sel))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lsel, err := metav1.LabelSelectorAsSelector(ls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ns, _ := client.Namespaced(opts.Path)
|
||||
oo, err := f.List("v1/pods", ns, lsel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(oo) > 1 {
|
||||
opts.MultiPods = true
|
||||
}
|
||||
|
||||
po := Pod{}
|
||||
po.Init(f, "v1/pods")
|
||||
for _, o := range oo {
|
||||
var pod v1.Pod
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &pod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug().Msgf("TAILING logs on pod %q", pod.Name)
|
||||
opts.Path = client.FQN(pod.Namespace, pod.Name)
|
||||
if err := po.TailLogs(ctx, c, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func toSelector(m map[string]string) string {
|
||||
s := make([]string, 0, len(m))
|
||||
for k, v := range m {
|
||||
s = append(s, k+"="+v)
|
||||
}
|
||||
|
||||
return strings.Join(s, ",")
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
type Generic struct {
|
||||
Factory
|
||||
|
||||
gvr client.GVR
|
||||
}
|
||||
|
||||
func (r *Generic) Init(f Factory, gvr client.GVR) {
|
||||
r.Factory, r.gvr = f, gvr
|
||||
}
|
||||
|
||||
// Delete a Generic.
|
||||
func (g *Generic) Delete(path string, cascade, force bool) error {
|
||||
p := metav1.DeletePropagationOrphan
|
||||
if cascade {
|
||||
p = metav1.DeletePropagationBackground
|
||||
}
|
||||
|
||||
ns, n := client.Namespaced(path)
|
||||
opts := metav1.DeleteOptions{PropagationPolicy: &p}
|
||||
if ns != "-" {
|
||||
return g.dynClient().Namespace(ns).Delete(n, &opts)
|
||||
}
|
||||
return g.dynClient().Delete(n, &opts)
|
||||
}
|
||||
|
||||
func (g *Generic) dynClient() dynamic.NamespaceableResourceInterface {
|
||||
return g.Client().DynDialOrDie().Resource(g.gvr.AsGVR())
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/derailed/tview"
|
||||
runewidth "github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
func toPerc(v1, v2 float64) float64 {
|
||||
if v2 == 0 {
|
||||
return 0
|
||||
}
|
||||
return math.Round((v1 / v2) * 100)
|
||||
}
|
||||
|
||||
// Truncate a string to the given l and suffix ellipsis if needed.
|
||||
func Truncate(str string, width int) string {
|
||||
return runewidth.Truncate(str, width, string(tview.SemigraphicsHorizontalEllipsis))
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package k8s
|
||||
package dao
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
|
@ -19,18 +19,3 @@ func TestToPerc(t *testing.T) {
|
|||
assert.Equal(t, u.e, toPerc(u.v1, u.v2))
|
||||
}
|
||||
}
|
||||
|
||||
func TestToMB(t *testing.T) {
|
||||
uu := []struct {
|
||||
v int64
|
||||
e float64
|
||||
}{
|
||||
{0, 0},
|
||||
{2 * megaByte, 2},
|
||||
{10 * megaByte, 10},
|
||||
}
|
||||
|
||||
for _, u := range uu {
|
||||
assert.Equal(t, u.e, ToMB(u.v))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
type Job struct {
|
||||
Generic
|
||||
}
|
||||
|
||||
var _ Accessor = &Job{}
|
||||
var _ Loggable = &Job{}
|
||||
|
||||
// Logs tail logs for all pods represented by this Job.
|
||||
func (j *Job) TailLogs(ctx context.Context, c chan<- string, opts LogOptions) error {
|
||||
o, err := j.Get(string(j.gvr), opts.Path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var job batchv1.Job
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &job)
|
||||
if err != nil {
|
||||
return errors.New("expecting a job resource")
|
||||
}
|
||||
|
||||
if job.Spec.Selector == nil || len(job.Spec.Selector.MatchLabels) == 0 {
|
||||
return fmt.Errorf("No valid selector found on Job %s", opts.Path)
|
||||
}
|
||||
|
||||
return podLogs(ctx, c, job.Spec.Selector.MatchLabels, opts)
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/color"
|
||||
)
|
||||
|
||||
// LogOptions represent logger options.
|
||||
type LogOptions struct {
|
||||
Path string
|
||||
Container string
|
||||
Lines int64
|
||||
Color color.Paint
|
||||
Previous bool
|
||||
SingleContainer bool
|
||||
MultiPods bool
|
||||
}
|
||||
|
||||
// HasContainer checks if a container is present.
|
||||
func (o LogOptions) HasContainer() bool {
|
||||
return o.Container != ""
|
||||
}
|
||||
|
||||
// FixedSizeName returns a normalize fixed size pod name if possible.
|
||||
func (o LogOptions) FixedSizeName() string {
|
||||
_, n := client.Namespaced(o.Path)
|
||||
tokens := strings.Split(n, "-")
|
||||
if len(tokens) < 3 {
|
||||
return n
|
||||
}
|
||||
var s []string
|
||||
for i := 0; i < len(tokens)-1; i++ {
|
||||
s = append(s, tokens[i])
|
||||
}
|
||||
|
||||
return Truncate(strings.Join(s, "-"), 15) + "-" + tokens[len(tokens)-1]
|
||||
}
|
||||
|
||||
func colorize(c color.Paint, txt string) string {
|
||||
if c == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return color.Colorize(txt, c)
|
||||
}
|
||||
|
||||
// DecorateLog add a log header to display po/co information along with the log message.
|
||||
func (o LogOptions) DecorateLog(msg string) string {
|
||||
_, n := client.Namespaced(o.Path)
|
||||
if msg == "" {
|
||||
return msg
|
||||
}
|
||||
|
||||
if o.MultiPods {
|
||||
return colorize(o.Color, n+":"+o.Container+" ") + msg
|
||||
}
|
||||
|
||||
if !o.SingleContainer {
|
||||
return colorize(o.Color, o.Container+" ") + msg
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/color"
|
||||
"github.com/derailed/k9s/internal/watch"
|
||||
"github.com/rs/zerolog/log"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
const defaultTimeout = 1 * time.Second
|
||||
|
||||
// Pod represents a pod resource.
|
||||
type Pod struct {
|
||||
Generic
|
||||
}
|
||||
|
||||
var _ Accessor = &Pod{}
|
||||
var _ Loggable = &Pod{}
|
||||
|
||||
// Logs fetch container logs for a given pod and container.
|
||||
func (p *Pod) Logs(path string, opts *v1.PodLogOptions) *restclient.Request {
|
||||
ns, n := client.Namespaced(path)
|
||||
return p.Client().DialOrDie().CoreV1().Pods(ns).GetLogs(n, opts)
|
||||
}
|
||||
|
||||
// Containers returns all container names on pod
|
||||
func (p *Pod) Containers(path string, includeInit bool) ([]string, error) {
|
||||
o, err := p.Get("v1/pod", path, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pod v1.Pod
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cc := []string{}
|
||||
for _, c := range pod.Spec.Containers {
|
||||
cc = append(cc, c.Name)
|
||||
}
|
||||
|
||||
if includeInit {
|
||||
for _, c := range pod.Spec.InitContainers {
|
||||
cc = append(cc, c.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Logs tails a given container logs
|
||||
func (p *Pod) TailLogs(ctx context.Context, c chan<- string, opts LogOptions) error {
|
||||
if !opts.HasContainer() {
|
||||
return p.logs(ctx, c, opts)
|
||||
}
|
||||
return tailLogs(ctx, p, c, opts)
|
||||
}
|
||||
|
||||
// PodLogs tail logs for all containers in a running Pod.
|
||||
func (p *Pod) logs(ctx context.Context, c chan<- string, opts LogOptions) error {
|
||||
fac, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)
|
||||
if !ok {
|
||||
return errors.New("Expecting an informer")
|
||||
}
|
||||
o, err := fac.Get("v1/pods", opts.Path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var po v1.Pod
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po); err != nil {
|
||||
return err
|
||||
}
|
||||
opts.Color = asColor(po.Name)
|
||||
if len(po.Spec.InitContainers)+len(po.Spec.Containers) == 1 {
|
||||
opts.SingleContainer = true
|
||||
}
|
||||
|
||||
for _, co := range po.Spec.InitContainers {
|
||||
opts.Container = co.Name
|
||||
if err := p.TailLogs(ctx, c, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
rcos := loggableContainers(po.Status)
|
||||
for _, co := range po.Spec.Containers {
|
||||
if in(rcos, co.Name) {
|
||||
opts.Container = co.Name
|
||||
if err := p.TailLogs(ctx, c, opts); err != nil {
|
||||
log.Error().Err(err).Msgf("Getting logs for %s failed", co.Name)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tailLogs(ctx context.Context, logger Logger, c chan<- string, opts LogOptions) error {
|
||||
log.Debug().Msgf("Tailing logs for %q -- %q", opts.Path, opts.Container)
|
||||
o := v1.PodLogOptions{
|
||||
Container: opts.Container,
|
||||
Follow: true,
|
||||
TailLines: &opts.Lines,
|
||||
Previous: opts.Previous,
|
||||
}
|
||||
req := logger.Logs(opts.Path, &o)
|
||||
ctxt, cancelFunc := context.WithCancel(ctx)
|
||||
req.Context(ctxt)
|
||||
|
||||
var blocked int32 = 1
|
||||
go logsTimeout(cancelFunc, &blocked)
|
||||
|
||||
// This call will block if nothing is in the stream!!
|
||||
stream, err := req.Stream()
|
||||
atomic.StoreInt32(&blocked, 0)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Log stream failed for `%s", opts.Path)
|
||||
return fmt.Errorf("Unable to obtain log stream for %s", opts.Path)
|
||||
}
|
||||
go readLogs(ctx, stream, c, opts)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func logsTimeout(cancel context.CancelFunc, blocked *int32) {
|
||||
<-time.After(defaultTimeout)
|
||||
if atomic.LoadInt32(blocked) == 1 {
|
||||
log.Debug().Msg("Timed out reading the log stream")
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
func readLogs(ctx context.Context, stream io.ReadCloser, c chan<- string, opts LogOptions) {
|
||||
defer func() {
|
||||
log.Debug().Msgf(">>> Closing stream `%s", opts.Path)
|
||||
if err := stream.Close(); err != nil {
|
||||
log.Error().Err(err).Msg("Cloing stream")
|
||||
}
|
||||
}()
|
||||
|
||||
scanner := bufio.NewScanner(stream)
|
||||
for scanner.Scan() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
c <- opts.DecorateLog(scanner.Text())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func loggableContainers(s v1.PodStatus) []string {
|
||||
var rcos []string
|
||||
for _, c := range s.ContainerStatuses {
|
||||
rcos = append(rcos, c.Name)
|
||||
}
|
||||
return rcos
|
||||
}
|
||||
|
||||
func asColor(n string) color.Paint {
|
||||
var sum int
|
||||
for _, r := range n {
|
||||
sum += int(r)
|
||||
}
|
||||
return color.Paint(30 + 2 + sum%6)
|
||||
}
|
||||
|
||||
// Check if string is in a string list.
|
||||
func in(ll []string, s string) bool {
|
||||
for _, l := range ll {
|
||||
if l == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
package k8s
|
||||
package dao
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
|
|
@ -19,18 +19,16 @@ import (
|
|||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/portforward"
|
||||
"k8s.io/client-go/transport/spdy"
|
||||
"k8s.io/kubectl/pkg/util"
|
||||
)
|
||||
|
||||
const localhost = "localhost"
|
||||
|
||||
// PortForward tracks a port forward stream.
|
||||
type PortForward struct {
|
||||
Connection
|
||||
// PortForwarder tracks a port forward stream.
|
||||
type PortForwarder struct {
|
||||
client.Connection
|
||||
genericclioptions.IOStreams
|
||||
|
||||
stopChan, readyChan chan struct{}
|
||||
logger *zerolog.Logger
|
||||
active bool
|
||||
path string
|
||||
container string
|
||||
|
|
@ -38,63 +36,62 @@ type PortForward struct {
|
|||
age time.Time
|
||||
}
|
||||
|
||||
// NewPortForward returns a new port forward streamer.
|
||||
func NewPortForward(c Connection, l *zerolog.Logger) *PortForward {
|
||||
return &PortForward{
|
||||
// NewPortForwarder returns a new port forward streamer.
|
||||
func NewPortForwarder(c client.Connection) *PortForwarder {
|
||||
return &PortForwarder{
|
||||
Connection: c,
|
||||
logger: l,
|
||||
stopChan: make(chan struct{}),
|
||||
readyChan: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Age returns the port forward age.
|
||||
func (p *PortForward) Age() string {
|
||||
func (p *PortForwarder) Age() string {
|
||||
return time.Since(p.age).String()
|
||||
}
|
||||
|
||||
// Active returns the forward status.
|
||||
func (p *PortForward) Active() bool {
|
||||
func (p *PortForwarder) Active() bool {
|
||||
return p.active
|
||||
}
|
||||
|
||||
// SetActive mark a portforward as active.
|
||||
func (p *PortForward) SetActive(b bool) {
|
||||
func (p *PortForwarder) SetActive(b bool) {
|
||||
p.active = b
|
||||
}
|
||||
|
||||
// Ports returns the forwarded ports mappings.
|
||||
func (p *PortForward) Ports() []string {
|
||||
func (p *PortForwarder) Ports() []string {
|
||||
return p.ports
|
||||
}
|
||||
|
||||
// Path returns the pod resource path.
|
||||
func (p *PortForward) Path() string {
|
||||
return p.path
|
||||
func (p *PortForwarder) Path() string {
|
||||
return p.path + ":" + p.container
|
||||
}
|
||||
|
||||
// Container returns the targetes container.
|
||||
func (p *PortForward) Container() string {
|
||||
func (p *PortForwarder) Container() string {
|
||||
return p.container
|
||||
}
|
||||
|
||||
// Stop terminates a port forard
|
||||
func (p *PortForward) Stop() {
|
||||
p.logger.Debug().Msgf("<<< Stopping port forward %q %v", p.path, p.ports)
|
||||
func (p *PortForwarder) Stop() {
|
||||
log.Debug().Msgf("<<< Stopping PortForward %q %v", p.path, p.ports)
|
||||
p.active = false
|
||||
close(p.stopChan)
|
||||
}
|
||||
|
||||
// FQN returns the portforward unique id.
|
||||
func (p *PortForward) FQN() string {
|
||||
func (p *PortForwarder) FQN() string {
|
||||
return p.path + ":" + p.container
|
||||
}
|
||||
|
||||
// Start initiates a port forward session for a given pod and ports.
|
||||
func (p *PortForward) Start(path, co string, ports []string) (*portforward.PortForwarder, error) {
|
||||
func (p *PortForwarder) Start(path, co, address string, ports []string) (*portforward.PortForwarder, error) {
|
||||
p.path, p.container, p.ports, p.age = path, co, ports, time.Now()
|
||||
|
||||
ns, n := namespaced(path)
|
||||
ns, n := client.Namespaced(path)
|
||||
pod, err := p.DialOrDie().CoreV1().Pods(ns).Get(n, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -110,7 +107,7 @@ func (p *PortForward) Start(path, co string, ports []string) (*portforward.PortF
|
|||
rcfg.NegotiatedSerializer = codec.WithoutConversion()
|
||||
clt, err := rest.RESTClientFor(rcfg)
|
||||
if err != nil {
|
||||
p.logger.Debug().Msgf("Boom! %#v", err)
|
||||
log.Debug().Msgf("Boom! %#v", err)
|
||||
return nil, err
|
||||
}
|
||||
req := clt.Post().
|
||||
|
|
@ -119,10 +116,10 @@ func (p *PortForward) Start(path, co string, ports []string) (*portforward.PortF
|
|||
Name(n).
|
||||
SubResource("portforward")
|
||||
|
||||
return p.forwardPorts("POST", req.URL(), ports)
|
||||
return p.forwardPorts("POST", req.URL(), address, ports)
|
||||
}
|
||||
|
||||
func (p *PortForward) forwardPorts(method string, url *url.URL, ports []string) (*portforward.PortForwarder, error) {
|
||||
func (p *PortForwarder) forwardPorts(method string, url *url.URL, address string, ports []string) (*portforward.PortForwarder, error) {
|
||||
cfg, err := p.Config().RESTConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -133,7 +130,10 @@ func (p *PortForward) forwardPorts(method string, url *url.URL, ports []string)
|
|||
}
|
||||
|
||||
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, method, url)
|
||||
addrs := []string{localhost}
|
||||
if address == "" {
|
||||
address = localhost
|
||||
}
|
||||
addrs := strings.Split(address, ",")
|
||||
return portforward.NewOnAddresses(dialer, addrs, ports, p.stopChan, p.readyChan, p.Out, p.ErrOut)
|
||||
}
|
||||
|
||||
|
|
@ -149,40 +149,3 @@ func codec() (serializer.CodecFactory, runtime.ParameterCodec) {
|
|||
|
||||
return serializer.NewCodecFactory(scheme), runtime.NewParameterCodec(scheme)
|
||||
}
|
||||
|
||||
func svcPortToTargetPort(ports []string, svc v1.Service, pod v1.Pod) ([]string, error) {
|
||||
var translated []string
|
||||
for _, port := range ports {
|
||||
localPort, remotePort := splitPort(port)
|
||||
portnum, err := strconv.Atoi(remotePort)
|
||||
if err != nil {
|
||||
svcPort, err := util.LookupServicePortNumberByName(svc, remotePort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
portnum = int(svcPort)
|
||||
if localPort == remotePort {
|
||||
localPort = strconv.Itoa(portnum)
|
||||
}
|
||||
}
|
||||
containerPort, err := util.LookupContainerPortNumberByServicePort(svc, pod, int32(portnum))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if int32(portnum) != containerPort {
|
||||
port = fmt.Sprintf("%s:%d", localPort, containerPort)
|
||||
}
|
||||
translated = append(translated, port)
|
||||
}
|
||||
|
||||
return translated, nil
|
||||
}
|
||||
|
||||
func splitPort(port string) (local, remote string) {
|
||||
parts := strings.Split(port, ":")
|
||||
if len(parts) == 2 {
|
||||
return parts[0], parts[1]
|
||||
}
|
||||
|
||||
return parts[0], parts[0]
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package dao
|
||||
|
||||
type PortForward struct {
|
||||
Generic
|
||||
}
|
||||
|
||||
var _ Accessor = &PortForward{}
|
||||
var _ Nuker = &PortForward{}
|
||||
|
||||
// Delete a portforward.
|
||||
func (p *PortForward) Delete(path string, cascade, force bool) error {
|
||||
p.Factory.DeleteForwarder(path)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// MetaViewers represents a collection of meta viewers.
|
||||
type ResourceMetas map[client.GVR]metav1.APIResource
|
||||
|
||||
// Accessors represents a collection of dao accessors.
|
||||
type Accessors map[client.GVR]Accessor
|
||||
|
||||
var resMetas = ResourceMetas{}
|
||||
|
||||
// AccessorFor returns a client accessor for a resource if registered.
|
||||
// Otherwise it returns a generic accessor.
|
||||
// Customize here for non resource types or types with metrics or logs.
|
||||
func AccessorFor(f Factory, gvr client.GVR) (Accessor, error) {
|
||||
m := Accessors{
|
||||
"contexts": &Context{},
|
||||
"containers": &Container{},
|
||||
"screendumps": &ScreenDump{},
|
||||
"benchmarks": &Benchmark{},
|
||||
"portforwards": &PortForward{},
|
||||
"v1/services": &Service{},
|
||||
"v1/pods": &Pod{},
|
||||
"apps/v1/deployments": &Deployment{},
|
||||
"apps/v1/daemonsets": &DaemonSet{},
|
||||
"extensions/v1beta1/daemonsets": &DaemonSet{},
|
||||
"apps/v1/statefulsets": &StatefulSet{},
|
||||
"batch/v1beta1/cronjobs": &CronJob{},
|
||||
"batch/v1/jobs": &Job{},
|
||||
}
|
||||
|
||||
r, ok := m[gvr]
|
||||
if !ok {
|
||||
r = &Generic{}
|
||||
log.Warn().Msgf("No DAO registry entry for %q. Using factory!", gvr)
|
||||
}
|
||||
r.Init(f, gvr)
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// RegisterMeta registers a new resource meta object.
|
||||
func RegisterMeta(gvr string, res metav1.APIResource) {
|
||||
resMetas[client.GVR(gvr)] = res
|
||||
}
|
||||
|
||||
func AllGVRs() client.GVRs {
|
||||
kk := make(client.GVRs, 0, len(resMetas))
|
||||
for k := range resMetas {
|
||||
kk = append(kk, k)
|
||||
}
|
||||
sort.Sort(kk)
|
||||
|
||||
return kk
|
||||
}
|
||||
|
||||
// MetaFor returns a resource metadata for a given gvr.
|
||||
func MetaFor(gvr client.GVR) (metav1.APIResource, error) {
|
||||
m, ok := resMetas[gvr]
|
||||
if !ok {
|
||||
return metav1.APIResource{}, fmt.Errorf("no resource meta defined for %q", gvr)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// IsK9sMeta checks for non resource meta.
|
||||
func IsK9sMeta(m metav1.APIResource) bool {
|
||||
for _, c := range m.Categories {
|
||||
if c == "k9s" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Load hydrates server preferred+CRDs resource metadata.
|
||||
func LoadResources(f Factory) error {
|
||||
resMetas = make(ResourceMetas, 100)
|
||||
if err := loadPreferred(f, resMetas); err != nil {
|
||||
return err
|
||||
}
|
||||
loadNonResource(resMetas)
|
||||
|
||||
return loadCRDs(f, resMetas)
|
||||
}
|
||||
|
||||
// BOZO!! Need contermeasure for direct commands!
|
||||
func loadNonResource(m ResourceMetas) {
|
||||
m["aliases"] = metav1.APIResource{
|
||||
Name: "aliases",
|
||||
Kind: "Aliases",
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["contexts"] = metav1.APIResource{
|
||||
Name: "contexts",
|
||||
Kind: "Contexts",
|
||||
ShortNames: []string{"ctx"},
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["screendumps"] = metav1.APIResource{
|
||||
Name: "screendumps",
|
||||
Kind: "ScreenDumps",
|
||||
ShortNames: []string{"sd"},
|
||||
Verbs: []string{"delete"},
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["benchmarks"] = metav1.APIResource{
|
||||
Name: "benchmarks",
|
||||
Kind: "Benchmarks",
|
||||
ShortNames: []string{"be"},
|
||||
Verbs: []string{"delete"},
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["portforwards"] = metav1.APIResource{
|
||||
Name: "portforwards",
|
||||
Namespaced: true,
|
||||
Kind: "PortForwards",
|
||||
ShortNames: []string{"pf"},
|
||||
Verbs: []string{"delete"},
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["containers"] = metav1.APIResource{
|
||||
Name: "containers",
|
||||
Kind: "Containers",
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
|
||||
loadRBAC(m)
|
||||
}
|
||||
|
||||
func loadRBAC(m ResourceMetas) {
|
||||
m["rbac"] = metav1.APIResource{
|
||||
Name: "rbacs",
|
||||
Kind: "Rules",
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["policy"] = metav1.APIResource{
|
||||
Name: "policies",
|
||||
Kind: "Rules",
|
||||
Namespaced: true,
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["users"] = metav1.APIResource{
|
||||
Name: "users",
|
||||
Kind: "User",
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m["groups"] = metav1.APIResource{
|
||||
Name: "groups",
|
||||
Kind: "Group",
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
}
|
||||
|
||||
func loadPreferred(f Factory, m ResourceMetas) error {
|
||||
discovery, err := f.Client().CachedDiscovery()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rr, err := discovery.ServerPreferredResources()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, r := range rr {
|
||||
for _, res := range r.APIResources {
|
||||
gvr := client.FromGVAndR(r.GroupVersion, res.Name)
|
||||
log.Debug().Msgf("GVR %s", gvr)
|
||||
res.Group, res.Version = gvr.ToG(), gvr.ToV()
|
||||
m[gvr] = res
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadCRDs(f Factory, m ResourceMetas) error {
|
||||
oo, err := f.List("apiextensions.k8s.io/v1beta1/customresourcedefinitions", "", labels.Everything())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Fail CRDs load")
|
||||
return nil
|
||||
}
|
||||
f.WaitForCacheSync()
|
||||
|
||||
for _, o := range oo {
|
||||
meta, errs := extractMeta(o)
|
||||
if len(errs) > 0 {
|
||||
log.Error().Err(errs[0]).Msgf("Fail to extract CRD meta (%d) errors", len(errs))
|
||||
continue
|
||||
}
|
||||
gvr := client.NewGVR(meta.Group, meta.Version, meta.Name)
|
||||
m[gvr] = meta
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractMeta(o runtime.Object) (metav1.APIResource, []error) {
|
||||
var (
|
||||
m metav1.APIResource
|
||||
errs []error
|
||||
)
|
||||
|
||||
crd, ok := o.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return m, append(errs, fmt.Errorf("Expected CustomResourceDefinition, but got %T", o))
|
||||
}
|
||||
|
||||
var spec map[string]interface{}
|
||||
spec, errs = extractMap(crd.Object, "spec", errs)
|
||||
|
||||
var meta map[string]interface{}
|
||||
meta, errs = extractMap(crd.Object, "metadata", errs)
|
||||
m.Name, errs = extractStr(meta, "name", errs)
|
||||
|
||||
m.Group, errs = extractStr(spec, "group", errs)
|
||||
m.Version, errs = extractStr(spec, "version", errs)
|
||||
|
||||
var scope string
|
||||
scope, errs = extractStr(spec, "scope", errs)
|
||||
|
||||
m.Namespaced = isNamespaced(scope)
|
||||
|
||||
var names map[string]interface{}
|
||||
names, errs = extractMap(spec, "names", errs)
|
||||
m.Kind, errs = extractStr(names, "kind", errs)
|
||||
m.SingularName, errs = extractStr(names, "singular", errs)
|
||||
m.Name, errs = extractStr(names, "plural", errs)
|
||||
m.ShortNames, errs = extractSlice(names, "shortNames", errs)
|
||||
|
||||
return m, errs
|
||||
}
|
||||
|
||||
func isNamespaced(scope string) bool {
|
||||
return scope == "Namespaced"
|
||||
}
|
||||
|
||||
func extractSlice(m map[string]interface{}, n string, errs []error) ([]string, []error) {
|
||||
if m[n] == nil {
|
||||
return nil, errs
|
||||
}
|
||||
s, ok := m[n].([]string)
|
||||
if ok {
|
||||
return s, errs
|
||||
}
|
||||
|
||||
ii, ok := m[n].([]interface{})
|
||||
if !ok {
|
||||
return s, append(errs, fmt.Errorf("failed to extract slice %s -- %#v", n, m))
|
||||
}
|
||||
|
||||
ss := make([]string, len(ii))
|
||||
for i, name := range ii {
|
||||
ss[i], ok = name.(string)
|
||||
if !ok {
|
||||
return s, append(errs, fmt.Errorf("expecting string shortnames"))
|
||||
}
|
||||
}
|
||||
return s, errs
|
||||
}
|
||||
|
||||
func extractStr(m map[string]interface{}, n string, errs []error) (string, []error) {
|
||||
s, ok := m[n].(string)
|
||||
if !ok {
|
||||
return s, append(errs, fmt.Errorf("failed to extract string %s", n))
|
||||
}
|
||||
return s, errs
|
||||
}
|
||||
|
||||
func extractMap(m map[string]interface{}, n string, errs []error) (map[string]interface{}, []error) {
|
||||
v, ok := m[n].(map[string]interface{})
|
||||
if !ok {
|
||||
return v, append(errs, fmt.Errorf("failed to extract field %s", n))
|
||||
}
|
||||
return v, errs
|
||||
}
|
||||
|
|
@ -1,26 +1,21 @@
|
|||
package k8s
|
||||
package dao
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/user"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/restmapper"
|
||||
)
|
||||
|
||||
var (
|
||||
// RestMapping holds k8s resource mapping
|
||||
RestMapping = &RestMapper{}
|
||||
toFileName = regexp.MustCompile(`[^(\w/\.)]`)
|
||||
)
|
||||
// RestMapping holds k8s resource mapping
|
||||
var RestMapping = &RestMapper{}
|
||||
|
||||
// RestMapper map resource to REST mapping ie kind, group, version.
|
||||
type RestMapper struct {
|
||||
Connection
|
||||
client.Connection
|
||||
}
|
||||
|
||||
// ToRESTMapper map resources to kind, and map kind and version to interfaces for manipulating K8s objects.
|
||||
|
|
@ -34,19 +29,6 @@ func (r *RestMapper) ToRESTMapper() (meta.RESTMapper, error) {
|
|||
return expander, nil
|
||||
}
|
||||
|
||||
func toHostDir(host string) string {
|
||||
h := strings.Replace(strings.Replace(host, "https://", "", 1), "http://", "", 1)
|
||||
return toFileName.ReplaceAllString(h, "_")
|
||||
}
|
||||
|
||||
func mustHomeDir() string {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return usr.HomeDir
|
||||
}
|
||||
|
||||
// ResourceFor produces a rest mapping from a given resource.
|
||||
// Support full res name ie deployment.v1.apps.
|
||||
func (r *RestMapper) ResourceFor(resourceArg, kind string) (*meta.RESTMapping, error) {
|
||||
|
|
@ -73,7 +55,6 @@ func (r *RestMapper) resourceFor(resourceArg string) (schema.GroupVersionResourc
|
|||
}
|
||||
|
||||
fullGVR, gr := schema.ParseResourceArg(strings.ToLower(resourceArg))
|
||||
log.Debug().Msgf("GVR %#v -- %#v", fullGVR, gr)
|
||||
if fullGVR != nil {
|
||||
return mapper.ResourceFor(*fullGVR)
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
type ScreenDump struct {
|
||||
Generic
|
||||
}
|
||||
|
||||
var _ Accessor = &ScreenDump{}
|
||||
var _ Nuker = &ScreenDump{}
|
||||
|
||||
// Delete a ScreenDump.
|
||||
func (d *ScreenDump) Delete(path string, cascade, force bool) error {
|
||||
return os.Remove(path)
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubectl/pkg/polymorphichelpers"
|
||||
)
|
||||
|
||||
type StatefulSet struct {
|
||||
Generic
|
||||
}
|
||||
|
||||
var _ Accessor = &StatefulSet{}
|
||||
var _ Loggable = &StatefulSet{}
|
||||
var _ Restartable = &StatefulSet{}
|
||||
var _ Scalable = &StatefulSet{}
|
||||
|
||||
// Scale a StatefulSet.
|
||||
func (s *StatefulSet) Scale(path string, replicas int32) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
scale, err := s.Client().DialOrDie().AppsV1().StatefulSets(ns).GetScale(n, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scale.Spec.Replicas = replicas
|
||||
_, err = s.Client().DialOrDie().AppsV1().StatefulSets(ns).UpdateScale(n, scale)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Restart a StatefulSet rollout.
|
||||
func (s *StatefulSet) Restart(path string) error {
|
||||
o, err := s.Get(string(s.gvr), path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ds appsv1.StatefulSet
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
update, err := polymorphichelpers.ObjectRestarterFn(&ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.Client().DialOrDie().AppsV1().StatefulSets(ds.Namespace).Patch(ds.Name, types.StrategicMergePatchType, update)
|
||||
return err
|
||||
}
|
||||
|
||||
// Logs tail logs for all pods represented by this StatefulSet.
|
||||
func (s *StatefulSet) TailLogs(ctx context.Context, c chan<- string, opts LogOptions) error {
|
||||
o, err := s.Get(string(s.gvr), opts.Path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var dp appsv1.StatefulSet
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &dp)
|
||||
if err != nil {
|
||||
return errors.New("expecting StatefulSet resource")
|
||||
}
|
||||
|
||||
if dp.Spec.Selector == nil || len(dp.Spec.Selector.MatchLabels) == 0 {
|
||||
return fmt.Errorf("No valid selector found on StatefulSet %s", opts.Path)
|
||||
}
|
||||
|
||||
return podLogs(ctx, c, dp.Spec.Selector.MatchLabels, opts)
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
Generic
|
||||
}
|
||||
|
||||
var _ Accessor = &Service{}
|
||||
var _ Loggable = &Service{}
|
||||
|
||||
// Logs tail logs for all pods represented by this Service.
|
||||
func (s *Service) TailLogs(ctx context.Context, c chan<- string, opts LogOptions) error {
|
||||
o, err := s.Get(string(s.gvr), opts.Path, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var svc v1.Service
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &svc)
|
||||
if err != nil {
|
||||
return errors.New("expecting Service resource")
|
||||
}
|
||||
|
||||
if svc.Spec.Selector == nil || len(svc.Spec.Selector) == 0 {
|
||||
return fmt.Errorf("no valid selector found on Service %s", opts.Path)
|
||||
}
|
||||
|
||||
return podLogs(ctx, c, svc.Spec.Selector, opts)
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/watch"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/informers"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
type Factory interface {
|
||||
// Client retrieves an api client.
|
||||
Client() client.Connection
|
||||
|
||||
// Get fetch a given resource.
|
||||
Get(gvr, path string, sel labels.Selector) (runtime.Object, error)
|
||||
|
||||
// List fetch a collection of resources.
|
||||
List(ns, gvr string, sel labels.Selector) ([]runtime.Object, error)
|
||||
|
||||
// ForResource fetch an informer for a given resource.
|
||||
ForResource(ns, gvr string) informers.GenericInformer
|
||||
|
||||
// WaitForCacheSync synchronize the cache.
|
||||
WaitForCacheSync()
|
||||
|
||||
// DeleteForwarder deletes a pod forwarder.
|
||||
DeleteForwarder(path string)
|
||||
|
||||
// Forwards returns all portforwards.
|
||||
Forwarders() watch.Forwarders
|
||||
}
|
||||
|
||||
// Accessor represents an accessible k8s resource.
|
||||
type Accessor interface {
|
||||
Nuker
|
||||
|
||||
// Init the resource with a factory object.
|
||||
Init(Factory, client.GVR)
|
||||
}
|
||||
|
||||
// Loggable represents resources with logs.
|
||||
type Loggable interface {
|
||||
// TaiLogs streams resource logs.
|
||||
TailLogs(ctx context.Context, c chan<- string, opts LogOptions) error
|
||||
}
|
||||
|
||||
type Scalable interface {
|
||||
Scale(path string, replicas int32) error
|
||||
}
|
||||
|
||||
// Nuker represents a resource deleter.
|
||||
type Nuker interface {
|
||||
// Delete removes a resource from the api server.
|
||||
Delete(path string, cascade, force bool) error
|
||||
}
|
||||
|
||||
// Switchable represents a switchable resource.
|
||||
type Switchable interface {
|
||||
// Switch changes the active context.
|
||||
Switch(ctx string) error
|
||||
}
|
||||
|
||||
// Restartable represents a restartable resource.
|
||||
type Restartable interface {
|
||||
// Restart performs a rollout restart.
|
||||
Restart(path string) error
|
||||
}
|
||||
|
||||
// Runnable represents a runnable resource.
|
||||
type Runnable interface {
|
||||
// Run triggers a run.
|
||||
Run(path string) error
|
||||
}
|
||||
|
||||
// Loggers represents a resource that exposes logs.
|
||||
type Logger interface {
|
||||
Logs(path string, opts *v1.PodLogOptions) *restclient.Request
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package k8s
|
||||
|
||||
type base struct {
|
||||
}
|
||||
|
||||
func (b *base) Kill(ns, n string) error {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// Cluster represents a Kubernetes cluster.
|
||||
type Cluster struct {
|
||||
Connection
|
||||
|
||||
logger *zerolog.Logger
|
||||
}
|
||||
|
||||
// NewCluster instantiates a new cluster.
|
||||
func NewCluster(c Connection, l *zerolog.Logger) *Cluster {
|
||||
return &Cluster{c, l}
|
||||
}
|
||||
|
||||
// Version returns the current cluster git version.
|
||||
func (c *Cluster) Version() (string, error) {
|
||||
rev, err := c.ServerVersion()
|
||||
if err != nil {
|
||||
c.logger.Warn().Msgf("%s", err)
|
||||
return "", err
|
||||
}
|
||||
return rev.GitVersion, nil
|
||||
}
|
||||
|
||||
// ContextName returns the currently active context.
|
||||
func (c *Cluster) ContextName() string {
|
||||
ctx, err := c.Config().CurrentContextName()
|
||||
if err != nil {
|
||||
c.logger.Warn().Msgf("%s", err)
|
||||
return "N/A"
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
// ClusterName return the currently active cluster name.
|
||||
func (c *Cluster) ClusterName() string {
|
||||
ctx, err := c.Config().CurrentClusterName()
|
||||
if err != nil {
|
||||
c.logger.Warn().Msgf("%s", err)
|
||||
return "N/A"
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
// UserName returns the currently active user.
|
||||
func (c *Cluster) UserName() string {
|
||||
usr, err := c.Config().CurrentUserName()
|
||||
if err != nil {
|
||||
c.logger.Warn().Msgf("%s", err)
|
||||
return "N/A"
|
||||
}
|
||||
return usr
|
||||
}
|
||||
|
||||
// GetNodes get all available nodes in the cluster.
|
||||
func (c *Cluster) GetNodes() (*v1.NodeList, error) {
|
||||
return c.FetchNodes()
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// ClusterRole represents a Kubernetes ClusterRole
|
||||
type ClusterRole struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewClusterRole returns a new ClusterRole.
|
||||
func NewClusterRole(c Connection) *ClusterRole {
|
||||
return &ClusterRole{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a cluster role.
|
||||
func (c *ClusterRole) Get(_, n string) (interface{}, error) {
|
||||
return c.DialOrDie().RbacV1().ClusterRoles().Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all ClusterRoles on a cluster.
|
||||
func (c *ClusterRole) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := c.DialOrDie().RbacV1().ClusterRoles().List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a ClusterRole.
|
||||
func (c *ClusterRole) Delete(_, n string, cascade, force bool) error {
|
||||
return c.DialOrDie().RbacV1().ClusterRoles().Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// ClusterRoleBinding represents a Kubernetes ClusterRoleBinding
|
||||
type ClusterRoleBinding struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewClusterRoleBinding returns a new ClusterRoleBinding.
|
||||
func NewClusterRoleBinding(c Connection) *ClusterRoleBinding {
|
||||
return &ClusterRoleBinding{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a service.
|
||||
func (c *ClusterRoleBinding) Get(_, n string) (interface{}, error) {
|
||||
return c.DialOrDie().RbacV1().ClusterRoleBindings().Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all ClusterRoleBindings on a cluster.
|
||||
func (c *ClusterRoleBinding) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := c.DialOrDie().RbacV1().ClusterRoleBindings().List(opts)
|
||||
if err != nil {
|
||||
return Collection{}, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a ClusterRoleBinding.
|
||||
func (c *ClusterRoleBinding) Delete(_, n string, cascade, force bool) error {
|
||||
return c.DialOrDie().RbacV1().ClusterRoleBindings().Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
// NamedContext represents a named cluster context.
|
||||
type NamedContext struct {
|
||||
Name string
|
||||
Context *api.Context
|
||||
config *Config
|
||||
}
|
||||
|
||||
// NewNamedContext returns a new named context.
|
||||
func NewNamedContext(c *Config, n string, ctx *api.Context) *NamedContext {
|
||||
return &NamedContext{Name: n, Context: ctx, config: c}
|
||||
}
|
||||
|
||||
// MustCurrentContextName return the active context name.
|
||||
func (c *NamedContext) MustCurrentContextName() string {
|
||||
cl, err := c.config.CurrentContextName()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cl
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Context represents a Kubernetes Context.
|
||||
type Context struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewContext returns a new Context.
|
||||
func NewContext(c Connection) *Context {
|
||||
return &Context{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a Context.
|
||||
func (c *Context) Get(_, n string) (interface{}, error) {
|
||||
ctx, err := c.Config().GetContext(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &NamedContext{Name: n, Context: ctx}, nil
|
||||
}
|
||||
|
||||
// List all Contexts on the current cluster.
|
||||
func (c *Context) List(string, metav1.ListOptions) (Collection, error) {
|
||||
ctxs, err := c.Config().Contexts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make([]interface{}, 0, len(ctxs))
|
||||
for k, v := range ctxs {
|
||||
cc = append(cc, NewNamedContext(c.Config(), k, v))
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a Context.
|
||||
func (c *Context) Delete(_, n string, cascade, force bool) error {
|
||||
ctx, err := c.Config().CurrentContextName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx == n {
|
||||
return fmt.Errorf("trying to delete your current context %s", n)
|
||||
}
|
||||
return c.Config().DelContext(n)
|
||||
}
|
||||
|
||||
// MustCurrentContextName return the active context name.
|
||||
func (c *Context) MustCurrentContextName() string {
|
||||
cl, err := c.Config().CurrentContextName()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cl
|
||||
}
|
||||
|
||||
// Switch to another context.
|
||||
func (c *Context) Switch(ctx string) error {
|
||||
c.SwitchContextOrDie(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
// KubeUpdate modifies kubeconfig default context.
|
||||
func (c *Context) KubeUpdate(n string) error {
|
||||
config, err := c.Config().RawConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Switch(n)
|
||||
return clientcmd.ModifyConfig(
|
||||
clientcmd.NewDefaultPathOptions(), config, true,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// CustomResourceDefinition represents a Kubernetes CustomResourceDefinition
|
||||
type CustomResourceDefinition struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewCustomResourceDefinition returns a new CustomResourceDefinition.
|
||||
func NewCustomResourceDefinition(c Connection) *CustomResourceDefinition {
|
||||
return &CustomResourceDefinition{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a CustomResourceDefinition.
|
||||
func (c *CustomResourceDefinition) Get(_, n string) (interface{}, error) {
|
||||
return c.NSDialOrDie().Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all CustomResourceDefinitions in a given namespace.
|
||||
func (c *CustomResourceDefinition) List(_ string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := c.NSDialOrDie().List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a CustomResourceDefinition.
|
||||
func (c *CustomResourceDefinition) Delete(_, n string, cascade, force bool) error {
|
||||
return c.NSDialOrDie().Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
batchv1beta1 "k8s.io/api/batch/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
)
|
||||
|
||||
const maxJobNameSize = 42
|
||||
|
||||
// CronJob represents a Kubernetes CronJob.
|
||||
type CronJob struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewCronJob returns a new CronJob.
|
||||
func NewCronJob(c Connection) *CronJob {
|
||||
return &CronJob{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a CronJob.
|
||||
func (c *CronJob) Get(ns, n string) (interface{}, error) {
|
||||
return c.DialOrDie().BatchV1beta1().CronJobs(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all CronJobs in a given namespace.
|
||||
func (c *CronJob) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := c.DialOrDie().BatchV1beta1().CronJobs(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a CronJob.
|
||||
func (c *CronJob) Delete(ns, n string, cascade, force bool) error {
|
||||
return c.DialOrDie().BatchV1beta1().CronJobs(ns).Delete(n, nil)
|
||||
}
|
||||
|
||||
// Run the job associated with this cronjob.
|
||||
func (c *CronJob) Run(ns, n string) error {
|
||||
cj, err := c.Get(ns, n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cronJob := cj.(*batchv1beta1.CronJob)
|
||||
|
||||
var jobName = cronJob.Name
|
||||
if len(cronJob.Name) >= maxJobNameSize {
|
||||
jobName = cronJob.Name[0:maxJobNameSize]
|
||||
}
|
||||
|
||||
job := &batchv1.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: jobName + "-manual-" + rand.String(3),
|
||||
Namespace: ns,
|
||||
Labels: cronJob.Spec.JobTemplate.Labels,
|
||||
},
|
||||
Spec: cronJob.Spec.JobTemplate.Spec,
|
||||
}
|
||||
|
||||
_, err = c.DialOrDie().BatchV1().Jobs(ns).Create(job)
|
||||
return err
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubectl/pkg/polymorphichelpers"
|
||||
)
|
||||
|
||||
// Deployment represents a Kubernetes Deployment.
|
||||
type Deployment struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewDeployment returns a new Deployment.
|
||||
func NewDeployment(c Connection) *Deployment {
|
||||
return &Deployment{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a deployment.
|
||||
func (d *Deployment) Get(ns, n string) (interface{}, error) {
|
||||
return d.DialOrDie().AppsV1().Deployments(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all Deployments in a given namespace.
|
||||
func (d *Deployment) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := d.DialOrDie().AppsV1().Deployments(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a Deployment.
|
||||
func (d *Deployment) Delete(ns, n string, cascade, force bool) error {
|
||||
return d.DialOrDie().AppsV1().Deployments(ns).Delete(n, nil)
|
||||
}
|
||||
|
||||
// Scale a Deployment.
|
||||
func (d *Deployment) Scale(ns, n string, replicas int32) error {
|
||||
scale, err := d.DialOrDie().AppsV1().Deployments(ns).GetScale(n, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scale.Spec.Replicas = replicas
|
||||
_, err = d.DialOrDie().AppsV1().Deployments(ns).UpdateScale(n, scale)
|
||||
return err
|
||||
}
|
||||
|
||||
// Restart a Deployment rollout.
|
||||
func (d *Deployment) Restart(ns, n string) error {
|
||||
|
||||
dp, err := d.DialOrDie().AppsV1().Deployments(ns).Get(n, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
update, err := polymorphichelpers.ObjectRestarterFn(dp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = d.DialOrDie().AppsV1().Deployments(ns).Patch(dp.Name, types.StrategicMergePatchType, update)
|
||||
return err
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubectl/pkg/polymorphichelpers"
|
||||
)
|
||||
|
||||
// DaemonSet represents a Kubernetes DaemonSet
|
||||
type DaemonSet struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewDaemonSet returns a new DaemonSet.
|
||||
func NewDaemonSet(c Connection) *DaemonSet {
|
||||
return &DaemonSet{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a DaemonSet.
|
||||
func (d *DaemonSet) Get(ns, n string) (interface{}, error) {
|
||||
return d.DialOrDie().AppsV1().DaemonSets(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all DaemonSets in a given namespace.
|
||||
func (d *DaemonSet) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := d.DialOrDie().AppsV1().DaemonSets(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a DaemonSet.
|
||||
func (d *DaemonSet) Delete(ns, n string, cascade, force bool) error {
|
||||
p := metav1.DeletePropagationOrphan
|
||||
if cascade {
|
||||
p = metav1.DeletePropagationBackground
|
||||
}
|
||||
return d.DialOrDie().AppsV1().DaemonSets(ns).Delete(n, &metav1.DeleteOptions{
|
||||
PropagationPolicy: &p,
|
||||
})
|
||||
}
|
||||
|
||||
// Restart a DaemonSet rollout.
|
||||
func (d *DaemonSet) Restart(ns, n string) error {
|
||||
|
||||
ds, err := d.DialOrDie().AppsV1().DaemonSets(ns).Get(n, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
update, err := polymorphichelpers.ObjectRestarterFn(ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = d.DialOrDie().AppsV1().DaemonSets(ns).Patch(ds.Name, types.StrategicMergePatchType, update)
|
||||
return err
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Endpoints represents a Kubernetes Endpoints.
|
||||
type Endpoints struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewEndpoints returns a new Endpoints.
|
||||
func NewEndpoints(c Connection) *Endpoints {
|
||||
return &Endpoints{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a Endpoint.
|
||||
func (e *Endpoints) Get(ns, n string) (interface{}, error) {
|
||||
return e.DialOrDie().CoreV1().Endpoints(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all Endpoints in a given namespace.
|
||||
func (e *Endpoints) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := e.DialOrDie().CoreV1().Endpoints(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a Endpoint.
|
||||
func (e *Endpoints) Delete(ns, n string, cascade, force bool) error {
|
||||
return e.DialOrDie().CoreV1().Endpoints(ns).Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Event represents a Kubernetes Event.
|
||||
type Event struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewEvent returns a new Event.
|
||||
func NewEvent(c Connection) *Event {
|
||||
return &Event{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a Event.
|
||||
func (e *Event) Get(ns, n string) (interface{}, error) {
|
||||
return e.DialOrDie().CoreV1().Events(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all Events in a given namespace.
|
||||
func (e *Event) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := e.DialOrDie().CoreV1().Events(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete an Event.
|
||||
func (e *Event) Delete(ns, n string, cascade, force bool) error {
|
||||
return e.DialOrDie().CoreV1().Events(ns).Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// GVR represents a fully qualified kubernetes resource.
|
||||
type GVR string
|
||||
|
||||
// NewGVR returns a new gvr.
|
||||
func NewGVR(g, v, r string) GVR {
|
||||
return GVR(path.Join(g, v, r))
|
||||
}
|
||||
|
||||
// ToGVR returns a new gvr from a string.
|
||||
func ToGVR(gv, r string) GVR {
|
||||
return GVR(path.Join(gv, r))
|
||||
}
|
||||
|
||||
// ResName returns a full res name ie dp.v1.apps.
|
||||
func (g GVR) ResName() string {
|
||||
return g.ToR() + "." + g.ToV() + "." + g.ToG()
|
||||
}
|
||||
|
||||
// AsGR returns the group version.
|
||||
func (g GVR) AsGR() schema.GroupVersion {
|
||||
return schema.GroupVersion{
|
||||
Group: g.ToG(),
|
||||
Version: g.ToV(),
|
||||
}
|
||||
}
|
||||
|
||||
// AsGVR returns a schema gvr instance.
|
||||
func (g GVR) AsGVR() schema.GroupVersionResource {
|
||||
return schema.GroupVersionResource{
|
||||
Group: g.ToG(),
|
||||
Version: g.ToV(),
|
||||
Resource: g.ToR(),
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a GVR as a string.
|
||||
func (g GVR) String() string {
|
||||
return string(g)
|
||||
}
|
||||
|
||||
// ToV returns the resource version.
|
||||
func (g GVR) ToV() string {
|
||||
tokens := strings.Split(string(g), "/")
|
||||
if len(tokens) < 2 {
|
||||
return ""
|
||||
}
|
||||
return tokens[len(tokens)-2]
|
||||
}
|
||||
|
||||
// ToR returns the resource name.
|
||||
func (g GVR) ToR() string {
|
||||
tokens := strings.Split(string(g), "/")
|
||||
return tokens[len(tokens)-1]
|
||||
}
|
||||
|
||||
// ToG returns the resource group name.
|
||||
func (g GVR) ToG() string {
|
||||
tokens := strings.Split(string(g), "/")
|
||||
switch len(tokens) {
|
||||
case 3:
|
||||
return tokens[0]
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
package k8s_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func TestAsGR(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
gvr string
|
||||
e schema.GroupVersion
|
||||
}{
|
||||
"full": {"apps/v1/deployments", schema.GroupVersion{"apps", "v1"}},
|
||||
"core": {"v1/pods", schema.GroupVersion{"", "v1"}},
|
||||
"bork": {"users", schema.GroupVersion{"", ""}},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, k8s.GVR(u.gvr).AsGR())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewGVR(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
g, v, r string
|
||||
e string
|
||||
}{
|
||||
"full": {"apps", "v1", "deployments", "apps/v1/deployments"},
|
||||
"core": {"", "v1", "pods", "v1/pods"},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, k8s.NewGVR(u.g, u.v, u.r).String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToGVR(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
gv, r, e string
|
||||
}{
|
||||
"full": {"apps/v1", "deployments", "apps/v1/deployments"},
|
||||
"core": {"v1", "pods", "v1/pods"},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, k8s.ToGVR(u.gv, u.r).String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResName(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
gvr string
|
||||
e string
|
||||
}{
|
||||
"full": {"apps/v1/deployments", "deployments.v1.apps"},
|
||||
"core": {"v1/pods", "pods.v1."},
|
||||
"k9s": {"users", "users.."},
|
||||
"empty": {"", ".."},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, k8s.GVR(u.gvr).ResName())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToR(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
gvr string
|
||||
e string
|
||||
}{
|
||||
"full": {"apps/v1/deployments", "deployments"},
|
||||
"core": {"v1/pods", "pods"},
|
||||
"k9s": {"users", "users"},
|
||||
"empty": {"", ""},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, k8s.GVR(u.gvr).ToR())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToG(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
gvr string
|
||||
e string
|
||||
}{
|
||||
"full": {"apps/v1/deployments", "apps"},
|
||||
"core": {"v1/pods", ""},
|
||||
"k9s": {"users", ""},
|
||||
"empty": {"", ""},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, k8s.GVR(u.gvr).ToG())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToV(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
gvr string
|
||||
e string
|
||||
}{
|
||||
"full": {"apps/v1/deployments", "v1"},
|
||||
"core": {"v1beta1/pods", "v1beta1"},
|
||||
"k9s": {"users", ""},
|
||||
"empty": {"", ""},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, k8s.GVR(u.gvr).ToV())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToStringer(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
gvr string
|
||||
}{
|
||||
"full": {"apps/v1/deployments"},
|
||||
"core": {"v1beta1/pods"},
|
||||
"k9s": {"users"},
|
||||
"empty": {""},
|
||||
}
|
||||
|
||||
for k, u := range uu {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.gvr, k8s.GVR(u.gvr).String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"math"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const megaByte = 1024 * 1024
|
||||
|
||||
// ToMB converts bytes to megabytes.
|
||||
func ToMB(v int64) float64 {
|
||||
return float64(v) / megaByte
|
||||
}
|
||||
|
||||
func toPerc(v1, v2 float64) float64 {
|
||||
if v2 == 0 {
|
||||
return 0
|
||||
}
|
||||
return math.Round((v1 / v2) * 100)
|
||||
}
|
||||
|
||||
func namespaced(n string) (string, string) {
|
||||
ns, po := path.Split(n)
|
||||
|
||||
return strings.Trim(ns, "/"), po
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// HorizontalPodAutoscalerV1 represents am HorizontalPodAutoscaler.
|
||||
type HorizontalPodAutoscalerV1 struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewHorizontalPodAutoscalerV1 returns a new HorizontalPodAutoscaler.
|
||||
func NewHorizontalPodAutoscalerV1(c Connection) *HorizontalPodAutoscalerV1 {
|
||||
return &HorizontalPodAutoscalerV1{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a HorizontalPodAutoscaler.
|
||||
func (h *HorizontalPodAutoscalerV1) Get(ns, n string) (interface{}, error) {
|
||||
return h.DialOrDie().AutoscalingV1().HorizontalPodAutoscalers(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all HorizontalPodAutoscalers in a given namespace.
|
||||
func (h *HorizontalPodAutoscalerV1) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := h.DialOrDie().AutoscalingV1().HorizontalPodAutoscalers(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a HorizontalPodAutoscaler.
|
||||
func (h *HorizontalPodAutoscalerV1) Delete(ns, n string, cascade, force bool) error {
|
||||
return h.DialOrDie().AutoscalingV1().HorizontalPodAutoscalers(ns).Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog/log"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// HorizontalPodAutoscalerV2Beta1 represents am HorizontalPodAutoscaler.
|
||||
type HorizontalPodAutoscalerV2Beta1 struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewHorizontalPodAutoscalerV2Beta1 returns a new HorizontalPodAutoscaler.
|
||||
func NewHorizontalPodAutoscalerV2Beta1(c Connection) *HorizontalPodAutoscalerV2Beta1 {
|
||||
return &HorizontalPodAutoscalerV2Beta1{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a HorizontalPodAutoscaler.
|
||||
func (h *HorizontalPodAutoscalerV2Beta1) Get(ns, n string) (interface{}, error) {
|
||||
return h.DialOrDie().AutoscalingV2beta1().HorizontalPodAutoscalers(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all HorizontalPodAutoscalers in a given namespace.
|
||||
func (h *HorizontalPodAutoscalerV2Beta1) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := h.DialOrDie().AutoscalingV2beta1().HorizontalPodAutoscalers(ns).List(opts)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Beta1 Failed!")
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a HorizontalPodAutoscaler.
|
||||
func (h *HorizontalPodAutoscalerV2Beta1) Delete(ns, n string, cascade, force bool) error {
|
||||
return h.DialOrDie().AutoscalingV2beta1().HorizontalPodAutoscalers(ns).Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
var supportedAutoScalingAPIVersions = []string{"v2beta2", "v2beta1", "v1"}
|
||||
|
||||
// HorizontalPodAutoscalerV2Beta2 represents am HorizontalPodAutoscaler.
|
||||
type HorizontalPodAutoscalerV2Beta2 struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewHorizontalPodAutoscalerV2Beta2 returns a new HorizontalPodAutoscalerV2Beta2.
|
||||
func NewHorizontalPodAutoscalerV2Beta2(c Connection) *HorizontalPodAutoscalerV2Beta2 {
|
||||
return &HorizontalPodAutoscalerV2Beta2{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a HorizontalPodAutoscalerV2Beta2.
|
||||
func (h *HorizontalPodAutoscalerV2Beta2) Get(ns, n string) (interface{}, error) {
|
||||
return h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all HorizontalPodAutoscalerV2Beta2s in a given namespace.
|
||||
func (h *HorizontalPodAutoscalerV2Beta2) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a HorizontalPodAutoscalerV2Beta2.
|
||||
func (h *HorizontalPodAutoscalerV2Beta2) Delete(ns, n string, cascade, force bool) error {
|
||||
return h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Ingress represents a Kubernetes Ingress.
|
||||
type Ingress struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewIngress returns a new Ingress.
|
||||
func NewIngress(c Connection) *Ingress {
|
||||
return &Ingress{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a Ingress.
|
||||
func (i *Ingress) Get(ns, n string) (interface{}, error) {
|
||||
return i.DialOrDie().ExtensionsV1beta1().Ingresses(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all Ingresses in a given namespace.
|
||||
func (i *Ingress) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := i.DialOrDie().ExtensionsV1beta1().Ingresses(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a Ingress.
|
||||
func (i *Ingress) Delete(ns, n string, cascade, force bool) error {
|
||||
return i.DialOrDie().ExtensionsV1beta1().Ingresses(ns).Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
type (
|
||||
// Job represents a Kubernetes Job.
|
||||
Job struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// Loggable represents a K8s resource that has containers and can be logged.
|
||||
Loggable interface {
|
||||
Containers(ns, n string, includeInit bool) ([]string, error)
|
||||
Logs(ns, n string, opts *v1.PodLogOptions) *restclient.Request
|
||||
}
|
||||
)
|
||||
|
||||
// NewJob returns a new Job.
|
||||
func NewJob(c Connection) *Job {
|
||||
return &Job{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a Job.
|
||||
func (j *Job) Get(ns, n string) (interface{}, error) {
|
||||
return j.DialOrDie().BatchV1().Jobs(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all Jobs in a given namespace.
|
||||
func (j *Job) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := j.DialOrDie().BatchV1().Jobs(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a Job.
|
||||
func (j *Job) Delete(ns, n string, cascade, force bool) error {
|
||||
return j.DialOrDie().BatchV1().Jobs(ns).Delete(n, nil)
|
||||
}
|
||||
|
||||
// Containers returns all container names on job.
|
||||
func (j *Job) Containers(ns, n string, includeInit bool) ([]string, error) {
|
||||
pod, err := j.assocPod(ns, n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewPod(j).Containers(ns, pod, includeInit)
|
||||
}
|
||||
|
||||
// Logs fetch container logs for a given job and container.
|
||||
func (j *Job) Logs(ns, n string, opts *v1.PodLogOptions) *restclient.Request {
|
||||
pod, err := j.assocPod(ns, n)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return NewPod(j).Logs(ns, pod, opts)
|
||||
}
|
||||
|
||||
// Events retrieved jobs events.
|
||||
func (j *Job) Events(ns, n string) (*v1.EventList, error) {
|
||||
e := j.DialOrDie().CoreV1().Events(ns)
|
||||
return e.List(metav1.ListOptions{
|
||||
FieldSelector: e.GetFieldSelector(&n, &ns, nil, nil).String(),
|
||||
})
|
||||
}
|
||||
|
||||
func (j *Job) assocPod(ns, n string) (string, error) {
|
||||
ee, err := j.Events(ns, n)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, e := range ee.Items {
|
||||
if strings.Contains(e.Message, "Created pod: ") {
|
||||
return strings.TrimSpace(strings.Replace(e.Message, "Created pod: ", "", 1)), nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("unable to find associated pod name for job: %s/%s", ns, n)
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Node represents a Kubernetes node.
|
||||
type Node struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewNode returns a new Node.
|
||||
func NewNode(c Connection) *Node {
|
||||
return &Node{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a node.
|
||||
func (n *Node) Get(_, name string) (interface{}, error) {
|
||||
return n.DialOrDie().CoreV1().Nodes().Get(name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all nodes on the cluster.
|
||||
func (n *Node) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := n.DialOrDie().CoreV1().Nodes().List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a node.
|
||||
func (n *Node) Delete(_, name string, cascade, force bool) error {
|
||||
return n.DialOrDie().CoreV1().Nodes().Delete(name, nil)
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// NetworkPolicy represents a Kubernetes NetworkPolicy
|
||||
type NetworkPolicy struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewNetworkPolicy returns a new NetworkPolicy.
|
||||
func NewNetworkPolicy(c Connection) *NetworkPolicy {
|
||||
return &NetworkPolicy{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a NetworkPolicy.
|
||||
func (d *NetworkPolicy) Get(ns, n string) (interface{}, error) {
|
||||
return d.DialOrDie().NetworkingV1().NetworkPolicies(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all NetworkPolicys in a given namespace.
|
||||
func (d *NetworkPolicy) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := d.DialOrDie().NetworkingV1().NetworkPolicies(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a NetworkPolicy.
|
||||
func (d *NetworkPolicy) Delete(ns, n string, cascade, force bool) error {
|
||||
p := metav1.DeletePropagationOrphan
|
||||
if cascade {
|
||||
p = metav1.DeletePropagationBackground
|
||||
}
|
||||
return d.DialOrDie().NetworkingV1().NetworkPolicies(ns).Delete(n, &metav1.DeleteOptions{
|
||||
PropagationPolicy: &p,
|
||||
})
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Namespace represents a Kubernetes namespace.
|
||||
type Namespace struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewNamespace returns a new Namespace.
|
||||
func NewNamespace(c Connection) *Namespace {
|
||||
return &Namespace{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a active namespace.
|
||||
func (n *Namespace) Get(_, name string) (interface{}, error) {
|
||||
return n.DialOrDie().CoreV1().Namespaces().Get(name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all active namespaces on the cluster.
|
||||
func (n *Namespace) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := n.DialOrDie().CoreV1().Namespaces().List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a namespace.
|
||||
func (n *Namespace) Delete(_, name string, cascade, force bool) error {
|
||||
return n.DialOrDie().CoreV1().Namespaces().Delete(name, nil)
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// PodDisruptionBudget represents a Kubernetes PodDisruptionBudget.
|
||||
type PodDisruptionBudget struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewPodDisruptionBudget returns a new PodDisruptionBudget.
|
||||
func NewPodDisruptionBudget(c Connection) *PodDisruptionBudget {
|
||||
return &PodDisruptionBudget{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a pdb.
|
||||
func (p *PodDisruptionBudget) Get(ns, n string) (interface{}, error) {
|
||||
return p.DialOrDie().PolicyV1beta1().PodDisruptionBudgets(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all pdbs in a given namespace.
|
||||
func (p *PodDisruptionBudget) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := p.DialOrDie().PolicyV1beta1().PodDisruptionBudgets(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a pdb.
|
||||
func (p *PodDisruptionBudget) Delete(ns, n string, cascade, force bool) error {
|
||||
return p.DialOrDie().PolicyV1beta1().PodDisruptionBudgets(ns).Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog/log"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
const defaultKillGrace int64 = 5
|
||||
|
||||
// Pod represents a Kubernetes Pod.
|
||||
type Pod struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewPod returns a new Pod.
|
||||
func NewPod(c Connection) *Pod {
|
||||
return &Pod{base: &base{}, Connection: c}
|
||||
}
|
||||
|
||||
// Get a pod.
|
||||
func (p *Pod) Get(ns, name string) (interface{}, error) {
|
||||
return p.DialOrDie().CoreV1().Pods(ns).Get(name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all pods in a given namespace.
|
||||
func (p *Pod) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := p.DialOrDie().CoreV1().Pods(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a pod.
|
||||
func (p *Pod) Delete(ns, n string, cascade, force bool) error {
|
||||
log.Debug().Msgf("Killing Pod %s %t:%t", n, cascade, force)
|
||||
grace := defaultKillGrace
|
||||
if force {
|
||||
grace = 0
|
||||
}
|
||||
return p.DialOrDie().CoreV1().Pods(ns).Delete(n, &metav1.DeleteOptions{
|
||||
GracePeriodSeconds: &grace,
|
||||
})
|
||||
}
|
||||
|
||||
// Containers returns all container names on pod
|
||||
func (p *Pod) Containers(ns, n string, includeInit bool) ([]string, error) {
|
||||
po, err := p.DialOrDie().CoreV1().Pods(ns).Get(n, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cc := []string{}
|
||||
for _, c := range po.Spec.Containers {
|
||||
cc = append(cc, c.Name)
|
||||
}
|
||||
|
||||
if includeInit {
|
||||
for _, c := range po.Spec.InitContainers {
|
||||
cc = append(cc, c.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Logs fetch container logs for a given pod and container.
|
||||
func (p *Pod) Logs(ns, n string, opts *v1.PodLogOptions) *restclient.Request {
|
||||
return p.DialOrDie().CoreV1().Pods(ns).GetLogs(n, opts)
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// PersistentVolume represents a Kubernetes PersistentVolume.
|
||||
type PersistentVolume struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewPersistentVolume returns a new PersistentVolume.
|
||||
func NewPersistentVolume(c Connection) *PersistentVolume {
|
||||
return &PersistentVolume{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a PersistentVolume.
|
||||
func (p *PersistentVolume) Get(_, n string) (interface{}, error) {
|
||||
return p.DialOrDie().CoreV1().PersistentVolumes().Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all PersistentVolumes in a given namespace.
|
||||
func (p *PersistentVolume) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := p.DialOrDie().CoreV1().PersistentVolumes().List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a PersistentVolume.
|
||||
func (p *PersistentVolume) Delete(_, n string, cascade, force bool) error {
|
||||
return p.DialOrDie().CoreV1().PersistentVolumes().Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// PersistentVolumeClaim represents a Kubernetes PersistentVolumeClaim.
|
||||
type PersistentVolumeClaim struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewPersistentVolumeClaim returns a new PersistentVolumeClaim.
|
||||
func NewPersistentVolumeClaim(c Connection) *PersistentVolumeClaim {
|
||||
return &PersistentVolumeClaim{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a PersistentVolumeClaim.
|
||||
func (p *PersistentVolumeClaim) Get(ns, n string) (interface{}, error) {
|
||||
return p.DialOrDie().CoreV1().PersistentVolumeClaims(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all PersistentVolumeClaims in a given namespace.
|
||||
func (p *PersistentVolumeClaim) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := p.DialOrDie().CoreV1().PersistentVolumeClaims(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a PersistentVolumeClaim.
|
||||
func (p *PersistentVolumeClaim) Delete(ns, n string, cascade, force bool) error {
|
||||
return p.DialOrDie().CoreV1().PersistentVolumeClaims(ns).Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// ReplicationController represents a Kubernetes ReplicationController.
|
||||
type ReplicationController struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewReplicationController returns a new ReplicationController.
|
||||
func NewReplicationController(c Connection) *ReplicationController {
|
||||
return &ReplicationController{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a RC.
|
||||
func (r *ReplicationController) Get(ns, n string) (interface{}, error) {
|
||||
return r.DialOrDie().CoreV1().ReplicationControllers(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all RCs in a given namespace.
|
||||
func (r *ReplicationController) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := r.DialOrDie().CoreV1().ReplicationControllers(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a RC.
|
||||
func (r *ReplicationController) Delete(ns, n string, cascade, force bool) error {
|
||||
p := metav1.DeletePropagationOrphan
|
||||
if cascade {
|
||||
p = metav1.DeletePropagationBackground
|
||||
}
|
||||
return r.DialOrDie().CoreV1().ReplicationControllers(ns).Delete(n, &metav1.DeleteOptions{
|
||||
PropagationPolicy: &p,
|
||||
})
|
||||
}
|
||||
|
||||
// Scale a ReplicationController.
|
||||
func (r *ReplicationController) Scale(ns, n string, replicas int32) error {
|
||||
scale, err := r.DialOrDie().CoreV1().ReplicationControllers(ns).GetScale(n, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scale.Spec.Replicas = replicas
|
||||
_, err = r.DialOrDie().CoreV1().ReplicationControllers(ns).UpdateScale(n, scale)
|
||||
return err
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
// Resource represents a Kubernetes Resource
|
||||
type Resource struct {
|
||||
*base
|
||||
Connection
|
||||
|
||||
gvr GVR
|
||||
}
|
||||
|
||||
// NewResource returns a new Resource.
|
||||
func NewResource(c Connection, gvr GVR) *Resource {
|
||||
return &Resource{base: &base{}, Connection: c, gvr: gvr}
|
||||
}
|
||||
|
||||
// GetInfo returns info about apigroup.
|
||||
func (r *Resource) GetInfo() GVR {
|
||||
return r.gvr
|
||||
}
|
||||
|
||||
func (r *Resource) nsRes() dynamic.NamespaceableResourceInterface {
|
||||
return r.DynDialOrDie().Resource(r.gvr.AsGVR())
|
||||
}
|
||||
|
||||
// Get a Resource.
|
||||
func (r *Resource) Get(ns, n string) (interface{}, error) {
|
||||
return r.nsRes().Namespace(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all Resources in a given namespace.
|
||||
func (r *Resource) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
obj, err := r.listAll(ns, r.gvr.ToR())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Collection{obj.(*metav1beta1.Table)}, nil
|
||||
}
|
||||
|
||||
// Delete a Resource.
|
||||
func (r *Resource) Delete(ns, n string, cascade, force bool) error {
|
||||
return r.nsRes().Namespace(ns).Delete(n, nil)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
const gvFmt = "application/json;as=Table;v=%s;g=%s, application/json"
|
||||
|
||||
func (r *Resource) listAll(ns, n string) (runtime.Object, error) {
|
||||
a := fmt.Sprintf(gvFmt, metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName)
|
||||
_, codec := r.codec()
|
||||
|
||||
c, err := r.getClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.Get().
|
||||
SetHeader("Accept", a).
|
||||
Namespace(ns).
|
||||
Resource(n).
|
||||
VersionedParams(&metav1beta1.TableOptions{}, codec).
|
||||
Do().Get()
|
||||
}
|
||||
|
||||
func (r *Resource) getClient() (*rest.RESTClient, error) {
|
||||
crConfig := r.RestConfigOrDie()
|
||||
gv := r.gvr.AsGR()
|
||||
crConfig.GroupVersion = &gv
|
||||
crConfig.APIPath = "/apis"
|
||||
if len(r.gvr.ToG()) == 0 {
|
||||
crConfig.APIPath = "/api"
|
||||
}
|
||||
codec, _ := r.codec()
|
||||
crConfig.NegotiatedSerializer = codec.WithoutConversion()
|
||||
|
||||
crRestClient, err := rest.RESTClientFor(crConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return crRestClient, nil
|
||||
}
|
||||
|
||||
func (r *Resource) codec() (serializer.CodecFactory, runtime.ParameterCodec) {
|
||||
scheme := runtime.NewScheme()
|
||||
gv := r.gvr.AsGR()
|
||||
metav1.AddToGroupVersion(scheme, gv)
|
||||
scheme.AddKnownTypes(gv, &metav1beta1.Table{}, &metav1beta1.TableOptions{})
|
||||
scheme.AddKnownTypes(metav1beta1.SchemeGroupVersion, &metav1beta1.Table{}, &metav1beta1.TableOptions{})
|
||||
|
||||
return serializer.NewCodecFactory(scheme), runtime.NewParameterCodec(scheme)
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
// rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Role represents a Kubernetes Role.
|
||||
type Role struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewRole returns a new Role.
|
||||
func NewRole(c Connection) *Role {
|
||||
return &Role{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a Role.
|
||||
func (r *Role) Get(ns, n string) (interface{}, error) {
|
||||
return r.DialOrDie().RbacV1().Roles(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all Roles in a given namespace.
|
||||
func (r *Role) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := r.DialOrDie().RbacV1().Roles(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a Role.
|
||||
func (r *Role) Delete(ns, n string, cascade, force bool) error {
|
||||
return r.DialOrDie().RbacV1().Roles(ns).Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
// RoleBinding represents a Kubernetes RoleBinding.
|
||||
type RoleBinding struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewRoleBinding returns a new RoleBinding.
|
||||
func NewRoleBinding(c Connection) *RoleBinding {
|
||||
return &RoleBinding{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a RoleBinding.
|
||||
func (r *RoleBinding) Get(ns, n string) (interface{}, error) {
|
||||
return r.DialOrDie().RbacV1().RoleBindings(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all RoleBindings in a given namespace.
|
||||
func (r *RoleBinding) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := r.DialOrDie().RbacV1().RoleBindings(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a RoleBinding.
|
||||
func (r *RoleBinding) Delete(ns, n string, cascade, force bool) error {
|
||||
return r.DialOrDie().RbacV1().RoleBindings(ns).Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// ReplicaSet represents a Kubernetes ReplicaSet.
|
||||
type ReplicaSet struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewReplicaSet returns a new ReplicaSet.
|
||||
func NewReplicaSet(c Connection) *ReplicaSet {
|
||||
return &ReplicaSet{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a ReplicaSet.
|
||||
func (r *ReplicaSet) Get(ns, n string) (interface{}, error) {
|
||||
return r.DialOrDie().AppsV1().ReplicaSets(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all ReplicaSets in a given namespace.
|
||||
func (r *ReplicaSet) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := r.DialOrDie().AppsV1().ReplicaSets(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a ReplicaSet.
|
||||
func (r *ReplicaSet) Delete(ns, n string, cascade, force bool) error {
|
||||
return r.DialOrDie().AppsV1().ReplicaSets(ns).Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// ServiceAccount manages a Kubernetes ServiceAccount.
|
||||
type ServiceAccount struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewServiceAccount instantiates a new ServiceAccount.
|
||||
func NewServiceAccount(c Connection) *ServiceAccount {
|
||||
return &ServiceAccount{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a ServiceAccount.
|
||||
func (s *ServiceAccount) Get(ns, n string) (interface{}, error) {
|
||||
return s.DialOrDie().CoreV1().ServiceAccounts(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all ServiceAccounts in a given namespace.
|
||||
func (s *ServiceAccount) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := s.DialOrDie().CoreV1().ServiceAccounts(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
return cc, nil
|
||||
|
||||
}
|
||||
|
||||
// Delete a ServiceAccount.
|
||||
func (s *ServiceAccount) Delete(ns, n string, cascade, force bool) error {
|
||||
return s.DialOrDie().CoreV1().ServiceAccounts(ns).Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// StorageClass represents a Kubernetes StorageClass.
|
||||
type StorageClass struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewStorageClass returns a new StorageClass.
|
||||
func NewStorageClass(c Connection) *StorageClass {
|
||||
return &StorageClass{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a StorageClass.
|
||||
func (p *StorageClass) Get(_, n string) (interface{}, error) {
|
||||
return p.DialOrDie().StorageV1().StorageClasses().Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all StorageClasses in a given namespace.
|
||||
func (p *StorageClass) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := p.DialOrDie().StorageV1().StorageClasses().List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a StorageClass.
|
||||
func (p *StorageClass) Delete(_, n string, cascade, force bool) error {
|
||||
return p.DialOrDie().StorageV1().StorageClasses().Delete(n, nil)
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubectl/pkg/polymorphichelpers"
|
||||
)
|
||||
|
||||
// StatefulSet manages a Kubernetes StatefulSet.
|
||||
type StatefulSet struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewStatefulSet instantiates a new StatefulSet.
|
||||
func NewStatefulSet(c Connection) *StatefulSet {
|
||||
return &StatefulSet{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a StatefulSet.
|
||||
func (s *StatefulSet) Get(ns, n string) (interface{}, error) {
|
||||
return s.DialOrDie().AppsV1().StatefulSets(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all StatefulSets in a given namespace.
|
||||
func (s *StatefulSet) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := s.DialOrDie().AppsV1().StatefulSets(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a StatefulSet.
|
||||
func (s *StatefulSet) Delete(ns, n string, cascade, force bool) error {
|
||||
p := metav1.DeletePropagationOrphan
|
||||
if cascade {
|
||||
p = metav1.DeletePropagationBackground
|
||||
}
|
||||
return s.DialOrDie().AppsV1().StatefulSets(ns).Delete(n, &metav1.DeleteOptions{
|
||||
PropagationPolicy: &p,
|
||||
})
|
||||
}
|
||||
|
||||
// Scale a StatefulSet.
|
||||
func (s *StatefulSet) Scale(ns, n string, replicas int32) error {
|
||||
scale, err := s.DialOrDie().AppsV1().StatefulSets(ns).GetScale(n, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scale.Spec.Replicas = replicas
|
||||
_, err = s.DialOrDie().AppsV1().StatefulSets(ns).UpdateScale(n, scale)
|
||||
return err
|
||||
}
|
||||
|
||||
// Restart a StatefulSet rollout.
|
||||
func (s *StatefulSet) Restart(ns, n string) error {
|
||||
|
||||
sts, err := s.DialOrDie().AppsV1().StatefulSets(ns).Get(n, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
update, err := polymorphichelpers.ObjectRestarterFn(sts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.DialOrDie().AppsV1().StatefulSets(ns).Patch(sts.Name, types.StrategicMergePatchType, update)
|
||||
return err
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Service represents a Kubernetes Service.
|
||||
type Service struct {
|
||||
*base
|
||||
Connection
|
||||
}
|
||||
|
||||
// NewService returns a new Service.
|
||||
func NewService(c Connection) *Service {
|
||||
return &Service{&base{}, c}
|
||||
}
|
||||
|
||||
// Get a service.
|
||||
func (s *Service) Get(ns, n string) (interface{}, error) {
|
||||
return s.DialOrDie().CoreV1().Services(ns).Get(n, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// List all Services in a given namespace.
|
||||
func (s *Service) List(ns string, opts metav1.ListOptions) (Collection, error) {
|
||||
rr, err := s.DialOrDie().CoreV1().Services(ns).List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := make(Collection, len(rr.Items))
|
||||
for i, r := range rr.Items {
|
||||
cc[i] = r
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// Delete a Service.
|
||||
func (s *Service) Delete(ns, n string, cascade, force bool) error {
|
||||
return s.DialOrDie().CoreV1().Services(ns).Delete(n, nil)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue