File kubectl-dup-0.3.2.obscpio of Package kubectl-dup
07070100000000000041ED00000000000000000000000266C10ABB00000000000000000000000000000000000000000000001A00000000kubectl-dup-0.3.2/.github07070100000001000041ED00000000000000000000000266C10ABB00000000000000000000000000000000000000000000002400000000kubectl-dup-0.3.2/.github/workflows07070100000002000081A400000000000000000000000166C10ABB0000023B000000000000000000000000000000000000003000000000kubectl-dup-0.3.2/.github/workflows/release.ymlname: release
on:
push:
tags:
- 'v*.*.*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master
- name: Setup Go
uses: actions/setup-go@v5
- name: GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: '~> v2'
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Update new version in krew-index
uses: rajatjindal/krew-release-bot@v0.0.46
07070100000003000081A400000000000000000000000166C10ABB000001ED000000000000000000000000000000000000001D00000000kubectl-dup-0.3.2/.gitignore# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
/bin/*
/dist/*
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
07070100000004000081A400000000000000000000000166C10ABB000002E8000000000000000000000000000000000000002200000000kubectl-dup-0.3.2/.goreleaser.ymlversion: 2
before:
hooks:
- go mod tidy
builds:
- env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ignore:
- goos: windows
goarch: arm64
mod_timestamp: "{{ .CommitTimestamp }}"
binary: kubectl-dup
ldflags:
- -s -w
checksum:
name_template: "{{ .ProjectName }}_{{ .Tag }}_checksums.txt"
archives:
- format: tar.gz
name_template: "{{ .ProjectName }}_{{ .Tag }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
# use zip for windows archives
format_overrides:
- goos: windows
format: zip
changelog:
use: github
snapshot:
version_template: "{{ incminor .Tag }}-dev-{{ .ShortCommit }}"
07070100000005000081A400000000000000000000000166C10ABB00000624000000000000000000000000000000000000001D00000000kubectl-dup-0.3.2/.krew.yamlapiVersion: krew.googlecontainertools.github.com/v1alpha2
kind: Plugin
metadata:
name: dup
spec:
version: {{ .TagName }}
homepage: https://github.com/vash/dup
shortDescription: Duplicate existing Kubernetes resources
description: |
This plugin is designed for on-the-fly duplication of Kubernetes resources.
It focuses on providing a convenient way to edit resources before duplication,
with a specific emphasis on Pods to create a fine-tuned resource quickly.
This tool can be used for debugging running containers without them crashing,
and simplifying the administration and general interaction with Kubernetes clusters.
platforms:
- selector:
matchLabels:
os: darwin
arch: amd64
{{addURIAndSha "https://github.com/vash/dup/releases/download/{{ .TagName }}/dup_{{ .TagName }}_darwin_amd64.tar.gz" .TagName }}
bin: kubectl-dup
- selector:
matchLabels:
os: darwin
arch: arm64
{{addURIAndSha "https://github.com/vash/dup/releases/download/{{ .TagName }}/dup_{{ .TagName }}_darwin_arm64.tar.gz" .TagName }}
bin: kubectl-dup
- selector:
matchLabels:
os: linux
arch: amd64
{{addURIAndSha "https://github.com/vash/dup/releases/download/{{ .TagName }}/dup_{{ .TagName }}_linux_amd64.tar.gz" .TagName }}
bin: kubectl-dup
- selector:
matchLabels:
os: windows
arch: amd64
{{addURIAndSha "https://github.com/vash/dup/releases/download/{{ .TagName }}/dup_{{ .TagName }}_windows_amd64.zip" .TagName }}
bin: kubectl-dup.exe
07070100000006000081A400000000000000000000000166C10ABB0000042E000000000000000000000000000000000000001A00000000kubectl-dup-0.3.2/LICENSEMIT License
Copyright (c) 2024 Eliran Barnoy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
07070100000007000081A400000000000000000000000166C10ABB0000081D000000000000000000000000000000000000001C00000000kubectl-dup-0.3.2/README.md
# dup - kubectl Plugin for duplication of Kubernetes Resources
This plugin is designed for on-the-fly duplication of Kubernetes resources.
It focuses on providing a convenient way to edit resources before duplication,
with a specific emphasis on Pods to create a fine-tuned resource quickly.
This tool can be used for debugging running containers without them crashing,
and simplifying the administration and general interaction with Kubernetes clusters.
[](https://asciinema.org/a/C8Lr4dCXGOCA6Pc0GB7xXWSyG)
## Installation
### Using Krew (Kubectl Plugin Manager)
```bash
kubectl krew install dup
```
### Manual Installation
Download the latest release from the GitHub releases page.
Extract the binary from the archive.
Move the binary to a directory in your system's PATH.
### Usage
```bash
kubectl dup [options] <resource-type,resource-type-2> <resource-name> <generated-resource-name-prefix>
```
## Examples
```bash
# duplicate a pod of deployment "my-deployment" without opening edit window
kubectl dup deployment my-deployment -pk
# duplicate a specific pod, regardless of what it belongs to,
# and disable liveness probes on it.
kubectl dup pod my-pod -d
```
## Features
- Resource Duplication: Duplicate Kubernetes resources with ease.
- Interactive Editing: Open resources for manual editing before applying changes.
- Pod Focused: Designed to streamline debugging and administration of Pods.
## Options
- -d, --disable-probes Automatically disable readiness and liveness probes for duplicated pods.
- -h, --help: Display help information.
- -p, --pod: Duplicate pod of complex objects, supported objects: 'StatefulSet','Deployment','CronJob','Job'.
- -k, --skip-edit: Skip editing duplicated resource before creation
## Contributing
We welcome contributions! If you have any ideas, enhancements, or bug fixes,
feel free to open an issue or submit a pull request.
## License
This project is licensed under the MIT License - see the LICENSE file for details.
07070100000008000041ED00000000000000000000000266C10ABB00000000000000000000000000000000000000000000001600000000kubectl-dup-0.3.2/cmd07070100000009000081A400000000000000000000000166C10ABB00000A1E000000000000000000000000000000000000001E00000000kubectl-dup-0.3.2/cmd/root.gopackage cmd
import (
"errors"
"fmt"
"dup/pkg/editor"
"dup/pkg/util"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/completion"
)
var (
kubeconfig string
namespace string
deployment string
edit bool
SilentErr = errors.New("SilentErr")
)
func validateArgs(cmd *cobra.Command, args []string) error {
if len(args) < 1 || len(args) > 2 {
return fmt.Errorf("requires exactly 1 or 2 arguments")
}
for _, arg := range args {
if !util.IsValidPod(arg) {
return fmt.Errorf("invalid argument: %s", arg)
}
}
return nil
}
func NewRootCmd(ioStreams genericclioptions.IOStreams) *cobra.Command {
o := editor.NewEditOptions(ioStreams)
kubeConfigFlags := defaultConfigFlags().WithWarningPrinter(o.IOStreams)
matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
f := cmdutil.NewFactory(matchVersionKubeConfigFlags)
var rootCmd = &cobra.Command{
Use: "kubectl dup [options] <resource-type, ...resource-type-n> <resource> [output-resource-prefix]",
Short: "Duplicate a pod out of a Deployment",
ValidArgsFunction: completion.ResourceTypeAndNameCompletionFunc(f),
Args: cobra.RangeArgs(2, 3),
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, args, cmd))
cmdutil.CheckErr(o.Run())
},
}
rootCmd.Flags().BoolVarP(&o.DuplicateOptions.DuplicateInnerPod, "pod", "p", false, "Duplicate pod of resource, currently only applies for: 'StatefulSet','Deployment','CronJob','Job'")
rootCmd.Flags().BoolVarP(&o.DuplicateOptions.DisableProbes, "disable-probes", "d", true, "Disable Readiness and liveness probes for duplicated pods only (requires '-p' for complex resources)")
rootCmd.Flags().BoolVarP(&o.DuplicateOptions.LoopCommand, "command-loop", "l", false, "Changes running command to an infinite loop (currently : \"tail -f /dev/null\"")
rootCmd.Flags().BoolVarP(&o.SkipEdit, "skip-edit", "k", false, "Skip editing duplicated resource before creation")
rootCmd.Flags().BoolVar(&o.WindowsLineEndings, "windows-line-endings", o.WindowsLineEndings,
"Defaults to the line ending native to your platform.")
kubeConfigFlags.AddFlags(rootCmd.Flags())
matchVersionKubeConfigFlags.AddFlags(rootCmd.Flags())
cmdutil.AddValidateFlags(rootCmd)
o.PrintFlags.AddFlags(rootCmd)
return rootCmd
}
func defaultConfigFlags() *genericclioptions.ConfigFlags {
return genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag().WithDiscoveryBurst(300).WithDiscoveryQPS(50.0)
}
0707010000000A000081A400000000000000000000000166C10ABB00000E71000000000000000000000000000000000000001900000000kubectl-dup-0.3.2/go.modmodule dup
go 1.22.0
toolchain go1.22.6
require (
github.com/evanphx/json-patch v5.9.0+incompatible
github.com/google/uuid v1.6.0
github.com/spf13/cobra v1.8.1
k8s.io/api v0.31.0
k8s.io/apimachinery v0.31.0
k8s.io/cli-runtime v0.31.0
k8s.io/client-go v0.31.0
k8s.io/klog/v2 v2.130.1
k8s.io/kubectl v0.31.0
)
require (
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/chai2010/gettext-go v1.0.3 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/fatih/camelcase v1.0.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.starlark.net v0.0.0-20240725214946-42030a7cedce // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/oauth2 v0.22.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/term v0.23.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/time v0.6.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/component-base v0.31.0 // indirect
k8s.io/kube-openapi v0.0.0-20240812233141-91dab695df6f // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.17.3 // indirect
sigs.k8s.io/kustomize/kyaml v0.17.2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
0707010000000B000081A400000000000000000000000166C10ABB00005104000000000000000000000000000000000000001900000000kubectl-dup-0.3.2/go.sumgithub.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/chai2010/gettext-go v1.0.3 h1:9liNh8t+u26xl5ddmWLmsOsdNLwkdRTg5AG+JnTiM80=
github.com/chai2010/gettext-go v1.0.3/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4=
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc=
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/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/onsi/ginkgo/v2 v2.20.0 h1:PE84V2mHqoT1sglvHc8ZdQtPcwmvvt29WLEEO3xmdZw=
github.com/onsi/ginkgo/v2 v2.20.0/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
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=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.starlark.net v0.0.0-20240725214946-42030a7cedce h1:YyGqCjZtGZJ+mRPaenEiB87afEO2MFRzLiJNZ0Z0bPw=
go.starlark.net v0.0.0-20240725214946-42030a7cedce/go.mod h1:YKMCv9b1WrfWmeqdV5MAuEHWsu5iC+fe6kYl2sQjdI8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo=
k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE=
k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc=
k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/cli-runtime v0.31.0 h1:V2Q1gj1u3/WfhD475HBQrIYsoryg/LrhhK4RwpN+DhA=
k8s.io/cli-runtime v0.31.0/go.mod h1:vg3H94wsubuvWfSmStDbekvbla5vFGC+zLWqcf+bGDw=
k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8=
k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU=
k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs=
k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240812233141-91dab695df6f h1:bnWtxXWdAl5bVOCEPoNdvMkyj6cTW3zxHuwKIakuV9w=
k8s.io/kube-openapi v0.0.0-20240812233141-91dab695df6f/go.mod h1:G0W3eI9gG219NHRq3h5uQaRBl4pj4ZpwzRP5ti8y770=
k8s.io/kubectl v0.31.0 h1:kANwAAPVY02r4U4jARP/C+Q1sssCcN/1p9Nk+7BQKVg=
k8s.io/kubectl v0.31.0/go.mod h1:pB47hhFypGsaHAPjlwrNbvhXgmuAr01ZBvAIIUaI8d4=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/kustomize/api v0.17.3 h1:6GCuHSsxq7fN5yhF2XrC+AAr8gxQwhexgHflOAD/JJU=
sigs.k8s.io/kustomize/api v0.17.3/go.mod h1:TuDH4mdx7jTfK61SQ/j1QZM/QWR+5rmEiNjvYlhzFhc=
sigs.k8s.io/kustomize/kyaml v0.17.2 h1:+AzvoJUY0kq4QAhH/ydPHHMRLijtUKiyVyh7fOSshr0=
sigs.k8s.io/kustomize/kyaml v0.17.2/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
0707010000000C000041ED00000000000000000000000266C10ABB00000000000000000000000000000000000000000000001700000000kubectl-dup-0.3.2/imgs0707010000000D000081A400000000000000000000000166C10ABB00001742000000000000000000000000000000000000001F00000000kubectl-dup-0.3.2/imgs/dup.svg<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="1665.086px" height="567.029px" viewBox="0 0 1665.086 567.029" enable-background="new 0 0 1665.086 567.029"
xml:space="preserve">
<g>
<polygon fill="#FFFFFF" points="787.366,94.488 603.874,316.331 881.293,472.541 1054.531,256.159 "/>
<g>
<g>
<path fill="#FED93E" d="M897.023,242.629c9.98,0.062,17.896,3.685,23.761,10.88c5.864,7.196,8.75,17.4,8.674,30.632
c-0.047,6.396-1.026,12.163-2.955,17.299c-1.937,5.133-4.478,9.487-7.656,13.041c-3.165,3.559-6.806,6.301-10.939,8.232
c-4.138,1.915-8.481,2.871-13.041,2.838c-4.543-0.028-8.498-0.875-11.849-2.514c-3.357-1.654-6.75-3.888-10.203-6.731
l0.565,13.494l3.257,136.029l-11.897-9.969l-4.314-211.512l11.06,0.066l1.076,9.276l0.494,0.008
c3.163-3.024,6.785-5.627,10.866-7.818C887.989,243.686,892.367,242.601,897.023,242.629z M894.027,253.831
c-3.682-0.031-7.16,0.847-10.428,2.613c-3.262,1.77-6.53,4.493-9.807,8.152l-0.254,41.305c3.332,3.052,6.547,5.219,9.633,6.488
c3.073,1.258,6.242,1.9,9.494,1.929c3.147,0.022,6.105-0.693,8.873-2.139c2.781-1.442,5.211-3.495,7.276-6.134
c2.089-2.646,3.735-5.812,4.951-9.482c1.211-3.69,1.834-7.788,1.864-12.351c0.063-9.541-1.785-16.983-5.552-22.309
C906.319,256.557,900.972,253.87,894.027,253.831z"/>
<path fill="#FED93E" d="M738.673,322.61c-9.972-0.065-17.88-3.69-23.748-10.89c-5.859-7.192-8.744-17.396-8.666-30.628
c0.045-6.391,1.022-12.157,2.957-17.296c1.93-5.136,4.479-9.482,7.643-13.041c3.163-3.554,6.811-6.299,10.948-8.225
c4.129-1.924,8.471-2.868,13.024-2.852c4.549,0.031,8.51,0.881,11.856,2.524c3.344,1.641,6.746,3.886,10.206,6.734l-0.57-13.505
l0.685-107.991l13.192,0.635l-0.925,192.813l-11.057-0.073l-1.081-9.272l-0.487-0.005c-3.161,3.016-6.775,5.621-10.855,7.813
C747.705,321.55,743.339,322.638,738.673,322.61z M741.673,311.408c3.692,0.022,7.161-0.851,10.431-2.616
c3.257-1.768,6.532-4.484,9.801-8.149l0.266-41.305c-3.344-3.053-6.547-5.214-9.628-6.488c-3.087-1.259-6.254-1.903-9.508-1.929
c-3.14-0.017-6.1,0.696-8.873,2.141c-2.773,1.446-5.202,3.492-7.271,6.134c-2.077,2.647-3.727,5.804-4.942,9.475
c-1.219,3.685-1.837,7.797-1.864,12.351c-0.062,9.541,1.786,16.982,5.549,22.313C729.393,308.677,734.736,311.36,741.673,311.408
z"/>
<path fill="#FED93E" d="M825.355,242.176c8.447,0.051,14.581,2.688,18.391,7.926c3.822,5.223,5.694,12.927,5.628,23.119
l-0.316,50.083l-13.491-0.084l0.309-48.291c0.041-7.479-1.091-12.882-3.392-16.2c-2.314-3.332-6.078-4.998-11.272-5.037
c-4.126-0.025-7.759,1.009-10.916,3.105c-3.159,2.088-6.65,5.457-10.475,10.095l-0.353,56.099l-13.335-0.078l0.501-79.036
l11.054,0.078l1.067,12.356l0.483,0.003c3.715-4.311,7.615-7.75,11.693-10.33C815.011,243.4,819.817,242.143,825.355,242.176z"/>
<polygon fill="#FED93E" points="776.191,129.375 778.003,106.735 603.874,316.331 616.118,322.411 "/>
<polygon fill="#FED93E" points="876.363,454.39 877.537,470.674 603.874,316.331 623.133,312.935 "/>
</g>
</g>
<g>
<g>
<path fill="#5C2D88" d="M768.065,327.242c-9.982-0.062-17.896-3.688-23.763-10.884c-5.862-7.192-8.752-17.396-8.672-30.63
c0.049-6.394,1.026-12.161,2.955-17.297c1.935-5.133,4.479-9.487,7.655-13.041c3.163-3.559,6.804-6.304,10.941-8.232
c4.135-1.918,8.479-2.874,13.038-2.838c4.543,0.028,8.498,0.872,11.851,2.51c3.354,1.655,6.748,3.892,10.201,6.735l-0.565-13.494
l-2.852-140.136l11.492,14.074l4.314,211.513l-11.059-0.067l-1.074-9.275l-0.495-0.008c-3.161,3.024-6.784,5.627-10.869,7.818
C777.098,326.186,772.72,327.267,768.065,327.242z M771.06,316.037c3.682,0.034,7.16-0.844,10.428-2.613
c3.265-1.767,6.529-4.49,9.809-8.149l0.255-41.305c-3.333-3.055-6.547-5.219-9.639-6.49c-3.07-1.256-6.24-1.901-9.49-1.93
c-3.146-0.02-6.106,0.693-8.873,2.142c-2.782,1.442-5.212,3.492-7.277,6.131c-2.089,2.647-3.735,5.812-4.951,9.485
c-1.208,3.688-1.834,7.791-1.865,12.351c-0.062,9.538,1.79,16.982,5.553,22.309C758.771,313.312,764.115,315.998,771.06,316.037z
"/>
<path fill="#5C2D88" d="M926.414,247.261c9.971,0.062,17.883,3.69,23.751,10.886c5.859,7.193,8.745,17.397,8.663,30.632
c-0.041,6.388-1.022,12.157-2.957,17.296c-1.933,5.133-4.479,9.479-7.644,13.039c-3.164,3.553-6.81,6.301-10.944,8.224
c-4.132,1.927-8.474,2.871-13.027,2.852c-4.548-0.03-8.51-0.878-11.855-2.521c-3.341-1.644-6.746-3.889-10.207-6.737l0.57,13.508
l-0.686,107.989l-13.191-0.634l0.926-192.813l11.056,0.073l1.081,9.272l0.487,0.003c3.162-3.017,6.779-5.622,10.854-7.816
C917.382,248.317,921.748,247.233,926.414,247.261z M923.414,258.463c-3.692-0.025-7.159,0.851-10.43,2.617
c-3.257,1.764-6.533,4.483-9.802,8.146l-0.266,41.308c3.341,3.053,6.55,5.214,9.631,6.488c3.087,1.255,6.251,1.907,9.505,1.929
c3.14,0.015,6.103-0.696,8.873-2.145c2.773-1.444,5.206-3.488,7.273-6.13c2.075-2.647,3.724-5.804,4.943-9.475
c1.216-3.685,1.834-7.8,1.862-12.356c0.061-9.538-1.787-16.977-5.55-22.313C935.695,261.194,930.351,258.511,923.414,258.463z"/>
<path fill="#5C2D88" d="M839.735,327.692c-8.452-0.051-14.585-2.685-18.395-7.923c-3.822-5.225-5.695-12.93-5.628-23.119
l0.315-50.083l13.492,0.085l-0.305,48.287c-0.045,7.481,1.087,12.882,3.388,16.203c2.318,3.329,6.078,4.996,11.271,5.035
c4.127,0.024,7.76-1.01,10.917-3.104c3.161-2.091,6.65-5.457,10.478-10.095l0.352-56.098l13.333,0.075l-0.501,79.038
l-11.054-0.078l-1.067-12.359h-0.481c-3.718,4.309-7.617,7.746-11.695,10.327C850.076,326.468,845.27,327.726,839.735,327.692z"
/>
<polygon fill="#5C2D88" points="888.896,440.493 888.683,463.928 1061.212,253.54 1047.771,246.867 "/>
<polygon fill="#5C2D88" points="790.461,112.922 788.327,94.965 1050.626,248.284 1041.954,256.937 "/>
</g>
</g>
</g>
</svg>
0707010000000E000081A400000000000000000000000166C10ABB0000008E000000000000000000000000000000000000001B00000000kubectl-dup-0.3.2/justfiledup:
go mod download && GOOS=linux GOARCH=amd64 go build -a -ldflags="-s -w" -o ./bin/kubectl-dup .
clean:
rm -rf bin
test:
go test ...
0707010000000F000081A400000000000000000000000166C10ABB00000108000000000000000000000000000000000000001A00000000kubectl-dup-0.3.2/main.gopackage main
import (
cmd "dup/cmd"
"os"
"k8s.io/cli-runtime/pkg/genericiooptions"
)
func main() {
root := cmd.NewRootCmd(genericiooptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr})
if err := root.Execute(); err != nil {
os.Exit(1)
}
}
07070100000010000041ED00000000000000000000000266C10ABB00000000000000000000000000000000000000000000001600000000kubectl-dup-0.3.2/pkg07070100000011000041ED00000000000000000000000266C10ABB00000000000000000000000000000000000000000000002000000000kubectl-dup-0.3.2/pkg/duplicate07070100000012000081A400000000000000000000000166C10ABB0000111E000000000000000000000000000000000000002D00000000kubectl-dup-0.3.2/pkg/duplicate/duplicate.gopackage duplicate
import (
duputil "dup/pkg/util"
"fmt"
"strings"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/resource"
)
const LOOP_COMMAND = "tail -f /dev/null"
type PodOptions struct {
DuplicateInnerPod bool
DisableProbes bool
LoopCommand bool
Image string
}
func Clone(opts *PodOptions, objects []*resource.Info) ([]*runtime.Object, error) {
var ret []*runtime.Object
for i := range objects {
obj := objects[i]
objKind := obj.Object.GetObjectKind().GroupVersionKind().Kind
if hasPodSpec(objKind) {
dResource, err := cloneResourceWithPod(obj.Object, opts)
if err != nil {
return nil, err
}
ret = append(ret, dResource)
} else {
dResource, err := cloneGenericResource(obj.Object)
if err != nil {
return nil, err
}
ret = append(ret, dResource)
}
}
return ret, nil
}
func hasPodSpec(kind string) bool {
switch kind {
case "Deployment", "CronJob", "StatefulSet", "Job", "Pod":
return true
}
return false
}
func unstructuredToType(in runtime.Object, out runtime.Object) error {
unstructured, ok := in.(*unstructured.Unstructured)
if !ok {
return fmt.Errorf("Error converting runtime.Object to unstructured")
}
err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructured.Object, out)
if err != nil {
return fmt.Errorf("Error converting unstructured to Deploy: %v\n", err)
}
return nil
}
func cloneResourceWithPod(obj runtime.Object, opts *PodOptions) (*runtime.Object, error) {
var dupObject runtime.Object
var metadata *metav1.ObjectMeta
var spec *corev1.PodSpec
objType := obj.GetObjectKind().GroupVersionKind().Kind
switch objType {
case "StatefulSet":
dupObject = &appsv1.StatefulSet{}
unstructuredToType(obj, dupObject)
metadata, spec = extractPod[StatefulSetAdapter](StatefulSetAdapter{dupObject.(*appsv1.StatefulSet)})
case "Deployment":
dupObject = &appsv1.Deployment{}
unstructuredToType(obj, dupObject)
metadata, spec = extractPod[DeploymentAdapter](DeploymentAdapter{dupObject.(*appsv1.Deployment)})
case "CronJob":
dupObject = &batchv1.CronJob{}
unstructuredToType(obj, dupObject)
metadata, spec = extractPod[CronJobAdapter](CronJobAdapter{dupObject.(*batchv1.CronJob)})
case "Job":
dupObject = &batchv1.Job{}
unstructuredToType(obj, dupObject)
metadata, spec = extractPod[JobAdapter](JobAdapter{dupObject.(*batchv1.Job)})
case "Pod":
dupObject = &corev1.Pod{}
unstructuredToType(obj, dupObject)
metadata, spec = extractPod[PodAdapter](PodAdapter{dupObject.(*corev1.Pod)})
default:
return nil, fmt.Errorf("object type %s does not have PodSpec or is not supported", objType)
}
applyOptions(objType, spec, metadata, opts)
err := setSuffixedName(&dupObject)
if err != nil {
return nil, err
}
return &dupObject, nil
}
func setSuffixedName(obj *runtime.Object) error {
accessor := meta.NewAccessor()
name, err := accessor.Name(*obj)
if err != nil {
return err
}
suffixedName := duputil.GenerateResourceName(name)
accessor.SetName(*obj, suffixedName)
return nil
}
func cloneGenericResource(obj runtime.Object) (*runtime.Object, error) {
objCopy := obj.DeepCopyObject()
err := setSuffixedName(&objCopy)
if err != nil {
return nil, err
}
return &objCopy, nil
}
func applyOptions(kind string, spec *corev1.PodSpec, meta *metav1.ObjectMeta, opts *PodOptions) {
if opts != nil {
if opts.DisableProbes {
disableProbes(spec)
}
if opts.LoopCommand {
setCommand(spec)
}
if kind == "Pod" {
removeOwnership(meta)
}
}
}
func disableProbes(podSpec *corev1.PodSpec) {
containers := podSpec.Containers
for i := range containers {
containers[i].ReadinessProbe = nil
containers[i].LivenessProbe = nil
}
}
func setCommand(podSpec *corev1.PodSpec) {
command := strings.Split(LOOP_COMMAND, " ")
containers := podSpec.Containers
for i := range containers {
containers[i].Command = command
}
}
func removeOwnership(metadata *metav1.ObjectMeta) {
delete(metadata.Labels, "app.kubernetes.io/instance")
delete(metadata.Labels, "app.kubernetes.io/name")
metadata.OwnerReferences = nil
metadata.UID = ""
metadata.ResourceVersion = ""
}
07070100000013000081A400000000000000000000000166C10ABB0000069F000000000000000000000000000000000000002E00000000kubectl-dup-0.3.2/pkg/duplicate/podextract.gopackage duplicate
import (
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type MetadataSpecExtractor[T any] interface {
GetPodMetadata() *metav1.ObjectMeta
GetPodSpec() *corev1.PodSpec
}
func extractPod[T MetadataSpecExtractor[T]](obj T) (*metav1.ObjectMeta, *corev1.PodSpec) {
return obj.GetPodMetadata(), obj.GetPodSpec()
}
type StatefulSetAdapter struct {
*appsv1.StatefulSet
}
func (s StatefulSetAdapter) GetPodMetadata() *metav1.ObjectMeta {
return &s.StatefulSet.Spec.Template.ObjectMeta
}
func (s StatefulSetAdapter) GetPodSpec() *corev1.PodSpec {
return &s.StatefulSet.Spec.Template.Spec
}
type DeploymentAdapter struct {
*appsv1.Deployment
}
func (s DeploymentAdapter) GetPodMetadata() *metav1.ObjectMeta {
return &s.Deployment.Spec.Template.ObjectMeta
}
func (s DeploymentAdapter) GetPodSpec() *corev1.PodSpec {
return &s.Deployment.Spec.Template.Spec
}
type CronJobAdapter struct {
*batchv1.CronJob
}
func (s CronJobAdapter) GetPodMetadata() *metav1.ObjectMeta {
return &s.CronJob.Spec.JobTemplate.Spec.Template.ObjectMeta
}
func (s CronJobAdapter) GetPodSpec() *corev1.PodSpec {
return &s.CronJob.Spec.JobTemplate.Spec.Template.Spec
}
type JobAdapter struct {
*batchv1.Job
}
func (s JobAdapter) GetPodMetadata() *metav1.ObjectMeta {
return &s.Job.Spec.Template.ObjectMeta
}
func (s JobAdapter) GetPodSpec() *corev1.PodSpec {
return &s.Job.Spec.Template.Spec
}
type PodAdapter struct {
*corev1.Pod
}
func (s PodAdapter) GetPodMetadata() *metav1.ObjectMeta {
return &s.Pod.ObjectMeta
}
func (s PodAdapter) GetPodSpec() *corev1.PodSpec {
return &s.Pod.Spec
}
07070100000014000041ED00000000000000000000000266C10ABB00000000000000000000000000000000000000000000001D00000000kubectl-dup-0.3.2/pkg/editor07070100000015000081A400000000000000000000000166C10ABB00004909000000000000000000000000000000000000002C00000000kubectl-dup-0.3.2/pkg/editor/editoptions.go/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package editor
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"path/filepath"
goruntime "runtime"
"strings"
"dup/pkg/duplicate"
jsonpatch "github.com/evanphx/json-patch"
"github.com/spf13/cobra"
"k8s.io/klog/v2"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/cli-runtime/pkg/resource"
_ "k8s.io/client-go/plugin/pkg/client/auth"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/cmd/util/editor/crlf"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
)
var SupportedSubresources = []string{"status"}
var encoder = scheme.DefaultJSONEncoder()
// EditOptions contains all the options for running edit cli command.
type EditOptions struct {
PrintFlags *genericclioptions.PrintFlags
ToPrinter func(string) (printers.ResourcePrinter, error)
OutputPatch bool
WindowsLineEndings bool
SkipEdit bool
DuplicateOptions *duplicate.PodOptions
cmdutil.ValidateOptions
ValidationDirective string
OriginalResult *resource.Result
CmdNamespace string
ApplyAnnotation bool
ChangeCause string
managedFields map[types.UID][]metav1.ManagedFieldsEntry
genericiooptions.IOStreams
f cmdutil.Factory
editPrinterOptions *editPrinterOptions
updatedResultGetter func(data []byte) *resource.Result
FieldManager string
Subresource string
}
type DuplicateOptions struct {
DuplicatePod bool
RemoveProbes bool
Image string
}
// NewEditOptions returns an initialized EditOptions instance
func NewEditOptions(ioStreams genericiooptions.IOStreams) *EditOptions {
return &EditOptions{
PrintFlags: genericclioptions.NewPrintFlags("edited").WithTypeSetter(scheme.Scheme),
editPrinterOptions: &editPrinterOptions{
// create new editor-specific PrintFlags, with all
// output flags disabled, except json / yaml
printFlags: (&genericclioptions.PrintFlags{
JSONYamlPrintFlags: genericclioptions.NewJSONYamlPrintFlags(),
}).WithDefaultOutput("yaml"),
ext: ".yaml",
addHeader: true,
},
WindowsLineEndings: goruntime.GOOS == "windows",
IOStreams: ioStreams,
DuplicateOptions: &duplicate.PodOptions{},
}
}
type editPrinterOptions struct {
printFlags *genericclioptions.PrintFlags
ext string
addHeader bool
}
func (e *editPrinterOptions) Complete(fromPrintFlags *genericclioptions.PrintFlags) error {
if e.printFlags == nil {
return fmt.Errorf("missing PrintFlags in editor printer options")
}
// bind output format from existing printflags
if fromPrintFlags != nil && len(*fromPrintFlags.OutputFormat) > 0 {
e.printFlags.OutputFormat = fromPrintFlags.OutputFormat
}
// prevent a commented header at the top of the user's
// default editor if presenting contents as json.
if *e.printFlags.OutputFormat == "json" {
e.addHeader = false
e.ext = ".json"
return nil
}
// we default to yaml if check above is false, as only json or yaml are supported
e.addHeader = true
e.ext = ".yaml"
return nil
}
func (e *editPrinterOptions) PrintObj(obj runtime.Object, out io.Writer) error {
p, err := e.printFlags.ToPrinter()
if err != nil {
return err
}
return p.PrintObj(obj, out)
}
// Complete completes all the required options
func (o *EditOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Command) error {
var err error
o.f = f
o.editPrinterOptions.Complete(o.PrintFlags)
o.CmdNamespace, _, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
b := f.NewBuilder().
Unstructured().
ResourceTypeOrNameArgs(true, args...).
NamespaceParam(o.CmdNamespace).DefaultNamespace().
ContinueOnError().
Flatten().
Do()
err = b.Err()
if err != nil {
return err
}
objects, err := b.Infos()
if err != nil {
return err
}
resources, err := duplicate.Clone(o.DuplicateOptions, objects)
if err != nil {
return err
}
resourceObjects, err := objsBody(resources)
if err != nil {
return err
}
result, err := o.Build(resourceObjects, o.ValidationDirective)
if err != nil {
return err
}
o.OriginalResult = result
o.updatedResultGetter = func(data []byte) *resource.Result {
// resource builder to read objects from edited data
return f.NewBuilder().
Unstructured().
Stream(bytes.NewReader(data), "edited-file").
ContinueOnError().
Flatten().
Do()
}
o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {
o.PrintFlags.NamePrintFlags.Operation = operation
return o.PrintFlags.ToPrinter()
}
return nil
}
func (o *EditOptions) createResources(obj []*resource.Info) error {
visitor := resource.InfoListVisitor(obj)
if err := o.restoreManagedFields(obj); err != nil {
return err
}
// need to make sure the original namespace wasn't changed while editing
if err := visitor.Visit(resource.RequireNamespace(o.CmdNamespace)); err != nil {
return err
}
// iterate through all items to apply annotations
if err := o.visitAnnotation(visitor); err != nil {
return err
}
err := o.visitToCreate(visitor)
if err != nil {
return err
}
return nil
}
// Run performs the execution
func (o *EditOptions) Run() error {
// CreateDuplicatePod(context.Background(), ioStreams, clientset, deployment, namespace, podName, edit)
edit := NewDefaultEditor(editorEnvs())
// editFn is invoked for each edit session (once with a list for normal edit, once for each individual resource in a edit-on-create invocation)
editFn := func(obj []*resource.Info) error {
var (
results = editResults{}
edited = []byte{}
file string
err error
)
containsError := false
// loop until we succeed or cancel editing
for {
// get the object we're going to serialize as input to the editor
originalObj := obj[0].Object
// generate the file to edit
buf := &bytes.Buffer{}
var w io.Writer = buf
if o.WindowsLineEndings {
w = crlf.NewCRLFWriter(w)
}
if o.editPrinterOptions.addHeader {
results.header.writeTo(w)
}
if !containsError {
if err := o.extractManagedFields(originalObj); err != nil {
return preservedFile(err, results.file, o.ErrOut)
}
if err := o.editPrinterOptions.PrintObj(originalObj, w); err != nil {
return preservedFile(err, results.file, o.ErrOut)
}
} else {
// In case of an error, preserve the edited file.
// Remove the comments (header) from it since we already
// have included the latest header in the buffer above.
buf.Write(cmdutil.ManualStrip(edited))
}
// launch the editor
editedDiff := edited
edited, file, err = edit.LaunchTempFile(fmt.Sprintf("%s-edit-", filepath.Base(os.Args[0])), o.editPrinterOptions.ext, buf)
if err != nil {
return preservedFile(err, results.file, o.ErrOut)
}
// If we're retrying the loop because of an error, and no change was made in the file, short-circuit
if containsError && bytes.Equal(cmdutil.StripComments(editedDiff), cmdutil.StripComments(edited)) {
return preservedFile(fmt.Errorf("%s", "Edit cancelled, no valid changes were saved."), file, o.ErrOut)
}
// cleanup any file from the previous pass
if len(results.file) > 0 {
os.Remove(results.file)
}
klog.V(4).Infof("User edited:\n%s", string(edited))
// Apply validation
schema, err := o.f.Validator(o.ValidationDirective)
if err != nil {
return preservedFile(err, file, o.ErrOut)
}
err = schema.ValidateBytes(cmdutil.StripComments(edited))
if err != nil {
results = editResults{
file: file,
}
containsError = true
fmt.Fprintln(o.ErrOut, results.addError(apierrors.NewInvalid(corev1.SchemeGroupVersion.WithKind("").GroupKind(),
"", field.ErrorList{field.Invalid(nil, "The edited file failed validation", fmt.Sprintf("%v", err))}), obj[0]))
continue
}
lines, err := hasLines(bytes.NewBuffer(edited))
if err != nil {
return preservedFile(err, file, o.ErrOut)
}
if !lines {
os.Remove(file)
fmt.Fprintln(o.ErrOut, "Edit cancelled, saved file was empty.")
return nil
}
results = editResults{
file: file,
}
// parse the edited file
updatedInfos, err := o.updatedResultGetter(edited).Infos()
if err != nil {
// syntax error
containsError = true
results.header.reasons = append(results.header.reasons, editReason{head: fmt.Sprintf("The edited file had a syntax error: %v", err)})
continue
}
containsError = false
// restore managed fields to original object
if err := o.restoreManagedFields(obj); err != nil {
return preservedFile(err, file, o.ErrOut)
}
err = o.createResources(updatedInfos)
if err != nil {
return preservedFile(err, results.file, o.ErrOut)
}
if len(results.edit) == 0 {
if results.notfound == 0 {
os.Remove(file)
} else {
fmt.Fprintf(o.Out, "The edits you made on deleted resources have been saved to %q\n", file)
}
return nil
}
if len(results.header.reasons) > 0 {
containsError = true
}
}
}
return o.OriginalResult.Visit(func(info *resource.Info, err error) error {
objects := []*resource.Info{info}
if o.SkipEdit {
err := o.createResources(objects)
if err != nil {
return err
}
return nil
}
return editFn(objects)
})
}
func (o *EditOptions) Build(reader io.Reader, validate string) (*resource.Result, error) {
schema, err := o.f.Validator(validate)
if err != nil {
return nil, err
}
result := o.f.NewBuilder().
Unstructured().
Schema(schema).
Stream(reader, "").
Do()
err = result.Err()
if err != nil {
return nil, err
}
return result, nil
}
func objBody(obj runtime.Object) (io.ReadCloser, error) {
encoded, err := runtime.Encode(encoder, obj)
if err != nil {
return nil, err
}
return io.NopCloser(bytes.NewReader([]byte(encoded))), nil
}
func objsBody(objs []*runtime.Object) (io.Reader, error) {
var readers []io.Reader
for _, obj := range objs {
encoded, err := runtime.Encode(encoder, *obj)
if err != nil {
return nil, err
}
reader := bytes.NewReader([]byte(encoded))
readers = append(readers, reader)
}
return io.MultiReader(readers...), nil
}
func (o *EditOptions) extractManagedFields(obj runtime.Object) error {
o.managedFields = make(map[types.UID][]metav1.ManagedFieldsEntry)
if meta.IsListType(obj) {
err := meta.EachListItem(obj, func(obj runtime.Object) error {
uid, mf, err := clearManagedFields(obj)
if err != nil {
return err
}
o.managedFields[uid] = mf
return nil
})
return err
}
uid, mf, err := clearManagedFields(obj)
if err != nil {
return err
}
o.managedFields[uid] = mf
return nil
}
func clearManagedFields(obj runtime.Object) (types.UID, []metav1.ManagedFieldsEntry, error) {
metaObjs, err := meta.Accessor(obj)
if err != nil {
return "", nil, err
}
mf := metaObjs.GetManagedFields()
metaObjs.SetManagedFields(nil)
return metaObjs.GetUID(), mf, nil
}
func (o *EditOptions) restoreManagedFields(infos []*resource.Info) error {
for _, info := range infos {
metaObjs, err := meta.Accessor(info.Object)
if err != nil {
return err
}
mf := o.managedFields[metaObjs.GetUID()]
metaObjs.SetManagedFields(mf)
}
return nil
}
func (o *EditOptions) annotationPatch(update *resource.Info) error {
patch, _, patchType, err := GetApplyPatch(update.Object.(runtime.Unstructured))
if err != nil {
return err
}
mapping := update.ResourceMapping()
client, err := o.f.UnstructuredClientForMapping(mapping)
if err != nil {
return err
}
helper := resource.NewHelper(client, mapping).
WithFieldManager(o.FieldManager).
WithFieldValidation(o.ValidationDirective).
WithSubresource(o.Subresource)
_, err = helper.Patch(o.CmdNamespace, update.Name, patchType, patch, nil)
return err
}
// GetApplyPatch is used to get and apply patches
func GetApplyPatch(obj runtime.Unstructured) ([]byte, []byte, types.PatchType, error) {
beforeJSON, err := encodeToJSON(obj)
if err != nil {
return nil, []byte(""), types.MergePatchType, err
}
objCopy := obj.DeepCopyObject()
accessor := meta.NewAccessor()
annotations, err := accessor.Annotations(objCopy)
if err != nil {
return nil, beforeJSON, types.MergePatchType, err
}
if annotations == nil {
annotations = map[string]string{}
}
annotations[corev1.LastAppliedConfigAnnotation] = string(beforeJSON)
accessor.SetAnnotations(objCopy, annotations)
afterJSON, err := encodeToJSON(objCopy.(runtime.Unstructured))
if err != nil {
return nil, beforeJSON, types.MergePatchType, err
}
patch, err := jsonpatch.CreateMergePatch(beforeJSON, afterJSON)
return patch, beforeJSON, types.MergePatchType, err
}
func encodeToJSON(obj runtime.Unstructured) ([]byte, error) {
serialization, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
if err != nil {
return nil, err
}
js, err := yaml.ToJSON(serialization)
if err != nil {
return nil, err
}
return js, nil
}
func (o *EditOptions) visitToCreate(createVisitor resource.Visitor) error {
err := createVisitor.Visit(func(info *resource.Info, incomingErr error) error {
obj, err := resource.NewHelper(info.Client, info.Mapping).
WithFieldManager(o.FieldManager).
WithFieldValidation(o.ValidationDirective).
Create(info.Namespace, true, info.Object)
if err != nil {
return err
}
info.Refresh(obj, true)
printer, err := o.ToPrinter("created")
if err != nil {
return err
}
return printer.PrintObj(info.Object, o.Out)
})
return err
}
func (o *EditOptions) visitAnnotation(annotationVisitor resource.Visitor) error {
// iterate through all items to apply annotations
err := annotationVisitor.Visit(func(info *resource.Info, incomingErr error) error {
// put configuration annotation in "updates"
if o.ApplyAnnotation {
if err := util.CreateOrUpdateAnnotation(true, info.Object, scheme.DefaultJSONEncoder()); err != nil {
return err
}
}
return nil
})
return err
}
// editReason preserves a message about the reason this file must be edited again
type editReason struct {
head string
other []string
}
// editHeader includes a list of reasons the edit must be retried
type editHeader struct {
reasons []editReason
}
// writeTo outputs the current header information into a stream
func (h *editHeader) writeTo(w io.Writer) error {
fmt.Fprint(w, `# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
`)
for _, r := range h.reasons {
if len(r.other) > 0 {
fmt.Fprintf(w, "# %s:\n", hashOnLineBreak(r.head))
} else {
fmt.Fprintf(w, "# %s\n", hashOnLineBreak(r.head))
}
for _, o := range r.other {
fmt.Fprintf(w, "# * %s\n", hashOnLineBreak(o))
}
fmt.Fprintln(w, "#")
}
return nil
}
// editResults capture the result of an update
type editResults struct {
header editHeader
retryable int
notfound int
edit []*resource.Info
file string
}
func (r *editResults) addError(err error, info *resource.Info) string {
resourceString := info.Mapping.Resource.Resource
if len(info.Mapping.Resource.Group) > 0 {
resourceString = resourceString + "." + info.Mapping.Resource.Group
}
switch {
case apierrors.IsInvalid(err):
r.edit = append(r.edit, info)
reason := editReason{
head: fmt.Sprintf("%s %q was not valid", resourceString, info.Name),
}
if err, ok := err.(apierrors.APIStatus); ok {
if details := err.Status().Details; details != nil {
for _, cause := range details.Causes {
reason.other = append(reason.other, fmt.Sprintf("%s: %s", cause.Field, cause.Message))
}
}
}
r.header.reasons = append(r.header.reasons, reason)
return fmt.Sprintf("error: %s %q is invalid", resourceString, info.Name)
case apierrors.IsNotFound(err):
r.notfound++
return fmt.Sprintf("error: %s %q could not be found on the server", resourceString, info.Name)
default:
r.retryable++
return fmt.Sprintf("error: %s %q could not be patched: %v", resourceString, info.Name, err)
}
}
// preservedFile writes out a message about the provided file if it exists to the
// provided output stream when an error happens. Used to notify the user where
// their updates were preserved.
func preservedFile(err error, path string, out io.Writer) error {
if len(path) > 0 {
if _, err := os.Stat(path); !os.IsNotExist(err) {
fmt.Fprintf(out, "A copy of your changes has been stored to %q\n", path)
}
}
return err
}
// hasLines returns true if any line in the provided stream is non empty - has non-whitespace
// characters, or the first non-whitespace character is a '#' indicating a comment. Returns
// any errors encountered reading the stream.
func hasLines(r io.Reader) (bool, error) {
// TODO: if any files we read have > 64KB lines, we'll need to switch to bytes.ReadLine
// TODO: probably going to be secrets
s := bufio.NewScanner(r)
for s.Scan() {
if line := strings.TrimSpace(s.Text()); len(line) > 0 && line[0] != '#' {
return true, nil
}
}
if err := s.Err(); err != nil && err != io.EOF {
return false, err
}
return false, nil
}
// hashOnLineBreak returns a string built from the provided string by inserting any necessary '#'
// characters after '\n' characters, indicating a comment.
func hashOnLineBreak(s string) string {
r := ""
for i, ch := range s {
j := i + 1
if j < len(s) && ch == '\n' && s[j] != '#' {
r += "\n# "
} else {
r += string(ch)
}
}
return r
}
// editorEnvs returns an ordered list of env vars to check for editor preferences.
func editorEnvs() []string {
return []string{
"KUBE_EDITOR",
"EDITOR",
}
}
07070100000016000081A400000000000000000000000166C10ABB000011CC000000000000000000000000000000000000002700000000kubectl-dup-0.3.2/pkg/editor/editor.go/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package editor
import (
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"k8s.io/klog/v2"
"k8s.io/kubectl/pkg/util/term"
)
const (
// sorry, blame Git
// TODO: on Windows rely on 'start' to launch the editor associated
// with the given file type. If we can't because of the need of
// blocking, use a script with 'ftype' and 'assoc' to detect it.
defaultEditor = "vi"
defaultShell = "/bin/bash"
windowsEditor = "notepad"
windowsShell = "cmd"
)
// Editor holds the command-line args to fire up the editor
type Editor struct {
Args []string
Shell bool
}
// NewDefaultEditor creates a struct Editor that uses the OS environment to
// locate the editor program, looking at EDITOR environment variable to find
// the proper command line. If the provided editor has no spaces, or no quotes,
// it is treated as a bare command to be loaded. Otherwise, the string will
// be passed to the user's shell for execution.
func NewDefaultEditor(envs []string) Editor {
args, shell := defaultEnvEditor(envs)
return Editor{
Args: args,
Shell: shell,
}
}
func defaultEnvShell() []string {
shell := os.Getenv("SHELL")
if len(shell) == 0 {
shell = platformize(defaultShell, windowsShell)
}
flag := "-c"
if shell == windowsShell {
flag = "/C"
}
return []string{shell, flag}
}
func defaultEnvEditor(envs []string) ([]string, bool) {
var editor string
for _, env := range envs {
if len(env) > 0 {
editor = os.Getenv(env)
}
if len(editor) > 0 {
break
}
}
if len(editor) == 0 {
editor = platformize(defaultEditor, windowsEditor)
}
if !strings.Contains(editor, " ") {
return []string{editor}, false
}
if !strings.ContainsAny(editor, "\"'\\") {
return strings.Split(editor, " "), false
}
// rather than parse the shell arguments ourselves, punt to the shell
shell := defaultEnvShell()
return append(shell, editor), true
}
func (e Editor) args(path string) []string {
args := make([]string, len(e.Args))
copy(args, e.Args)
if e.Shell {
last := args[len(args)-1]
args[len(args)-1] = fmt.Sprintf("%s %q", last, path)
} else {
args = append(args, path)
}
return args
}
// Launch opens the described or returns an error. The TTY will be protected, and
// SIGQUIT, SIGTERM, and SIGINT will all be trapped.
func (e Editor) Launch(path string) error {
if len(e.Args) == 0 {
return fmt.Errorf("no editor defined, can't open %s", path)
}
abs, err := filepath.Abs(path)
if err != nil {
return err
}
args := e.args(abs)
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
klog.V(5).Infof("Opening file with editor %v", args)
if err := (term.TTY{In: os.Stdin, TryDev: true}).Safe(cmd.Run); err != nil {
if err, ok := err.(*exec.Error); ok {
if err.Err == exec.ErrNotFound {
return fmt.Errorf("unable to launch the editor %q", strings.Join(e.Args, " "))
}
}
return fmt.Errorf("there was a problem with the editor %q", strings.Join(e.Args, " "))
}
return nil
}
// LaunchTempFile reads the provided stream into a temporary file in the given directory
// and file prefix, and then invokes Launch with the path of that file. It will return
// the contents of the file after launch, any errors that occur, and the path of the
// temporary file so the caller can clean it up as needed.
func (e Editor) LaunchTempFile(prefix, suffix string, r io.Reader) ([]byte, string, error) {
f, err := os.CreateTemp("", prefix+"*"+suffix)
if err != nil {
return nil, "", err
}
defer f.Close()
path := f.Name()
if _, err := io.Copy(f, r); err != nil {
os.Remove(path)
return nil, path, err
}
// This file descriptor needs to close so the next process (Launch) can claim it.
f.Close()
if err := e.Launch(path); err != nil {
return nil, path, err
}
bytes, err := os.ReadFile(path)
return bytes, path, err
}
func platformize(linux, windows string) string {
if runtime.GOOS == "windows" {
return windows
}
return linux
}
07070100000017000081A400000000000000000000000166C10ABB0000192E000000000000000000000000000000000000002800000000kubectl-dup-0.3.2/pkg/editor/factory.gopackage editor
import (
"errors"
"sync"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
openapiclient "k8s.io/client-go/openapi"
"k8s.io/client-go/openapi/cached"
"k8s.io/client-go/rest"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util/openapi"
"k8s.io/kubectl/pkg/validation"
)
func NewFactory(clientGetter genericclioptions.RESTClientGetter) cmdutil.Factory {
if clientGetter == nil {
panic("attempt to instantiate client_access_factory with nil clientGetter")
}
f := &factoryImpl{
clientGetter: clientGetter,
}
return f
}
func (f *factoryImpl) KubernetesClientSet() (*kubernetes.Clientset, error) {
clientConfig, err := f.ToRESTConfig()
if err != nil {
return nil, err
}
return kubernetes.NewForConfig(clientConfig)
}
func setKubernetesDefaults(config *rest.Config) error {
// TODO remove this hack. This is allowing the GetOptions to be serialized.
config.GroupVersion = &schema.GroupVersion{Group: "", Version: "v1"}
if config.APIPath == "" {
config.APIPath = "/api"
}
if config.NegotiatedSerializer == nil {
// This codec factory ensures the resources are not converted. Therefore, resources
// will not be round-tripped through internal versions. Defaulting does not happen
// on the client.
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
}
return rest.SetKubernetesDefaults(config)
}
func (f *factoryImpl) ClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
cfg, err := f.clientGetter.ToRESTConfig()
if err != nil {
return nil, err
}
if err := setKubernetesDefaults(cfg); err != nil {
return nil, err
}
gvk := mapping.GroupVersionKind
switch gvk.Group {
case corev1.GroupName:
cfg.APIPath = "/api"
default:
cfg.APIPath = "/apis"
}
gv := gvk.GroupVersion()
cfg.GroupVersion = &gv
return restclient.RESTClientFor(cfg)
}
type factoryImpl struct {
clientGetter genericclioptions.RESTClientGetter
// Caches OpenAPI document and parsed resources
openAPIParser *openapi.CachedOpenAPIParser
oapi *openapi.CachedOpenAPIGetter
parser sync.Once
getter sync.Once
}
func (f *factoryImpl) Validator(validationDirective string) (validation.Schema, error) {
// client-side schema validation is only performed
// when the validationDirective is strict.
// If the directive is warn, we rely on the ParamVerifyingSchema
// to ignore the client-side validation and provide a warning
// to the user that attempting warn validation when SS validation
// is unsupported is inert.
if validationDirective == metav1.FieldValidationIgnore {
return validation.NullSchema{}, nil
}
schema := validation.ConjunctiveSchema{
validation.NewSchemaValidation(f),
validation.NoDoubleKeySchema{},
}
dynamicClient, err := f.DynamicClient()
if err != nil {
return nil, err
}
// Create the FieldValidationVerifier for use in the ParamVerifyingSchema.
discoveryClient, err := f.ToDiscoveryClient()
if err != nil {
return nil, err
}
// Memory-cache the OpenAPI V3 responses. The disk cache behavior is determined by
// the discovery client.
oapiV3Client := cached.NewClient(discoveryClient.OpenAPIV3())
queryParam := resource.QueryParamFieldValidation
primary := resource.NewQueryParamVerifierV3(dynamicClient, oapiV3Client, queryParam)
secondary := resource.NewQueryParamVerifier(dynamicClient, f.openAPIGetter(), queryParam)
fallback := resource.NewFallbackQueryParamVerifier(primary, secondary)
return validation.NewParamVerifyingSchema(schema, fallback, string(validationDirective)), nil
}
func (f *factoryImpl) openAPIGetter() discovery.OpenAPISchemaInterface {
discovery, err := f.clientGetter.ToDiscoveryClient()
if err != nil {
return nil
}
f.getter.Do(func() {
f.oapi = openapi.NewOpenAPIGetter(discovery)
})
return f.oapi
}
func (f *factoryImpl) OpenAPISchema() (openapi.Resources, error) {
openAPIGetter := f.openAPIGetter()
if openAPIGetter == nil {
return nil, errors.New("no openapi getter")
}
// Lazily initialize the OpenAPIParser once
f.parser.Do(func() {
// Create the caching OpenAPIParser
f.openAPIParser = openapi.NewOpenAPIParser(f.openAPIGetter())
})
// Delegate to the OpenAPIPArser
return f.openAPIParser.Parse()
}
func (f *factoryImpl) ToRESTConfig() (*restclient.Config, error) {
return f.clientGetter.ToRESTConfig()
}
func (f *factoryImpl) DynamicClient() (dynamic.Interface, error) {
clientConfig, err := f.ToRESTConfig()
if err != nil {
return nil, err
}
return dynamic.NewForConfig(clientConfig)
}
func (f *factoryImpl) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
return f.clientGetter.ToDiscoveryClient()
}
func (f *factoryImpl) NewBuilder() *resource.Builder {
// NewBuilder returns a new resource builder for structured api objects.
return resource.NewBuilder(f.clientGetter)
}
func (f *factoryImpl) OpenAPIV3Client() (openapiclient.Client, error) {
discovery, err := f.clientGetter.ToDiscoveryClient()
if err != nil {
return nil, err
}
return cached.NewClient(discovery.OpenAPIV3()), nil
}
func (f *factoryImpl) RESTClient() (*restclient.RESTClient, error) {
clientConfig, err := f.ToRESTConfig()
if err != nil {
return nil, err
}
setKubernetesDefaults(clientConfig)
return restclient.RESTClientFor(clientConfig)
}
func (f *factoryImpl) ToRESTMapper() (meta.RESTMapper, error) {
return f.clientGetter.ToRESTMapper()
}
func (f *factoryImpl) ToRawKubeConfigLoader() clientcmd.ClientConfig {
return f.clientGetter.ToRawKubeConfigLoader()
}
func (f *factoryImpl) UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
cfg, err := f.clientGetter.ToRESTConfig()
if err != nil {
return nil, err
}
if err := restclient.SetKubernetesDefaults(cfg); err != nil {
return nil, err
}
cfg.APIPath = "/apis"
if mapping.GroupVersionKind.Group == corev1.GroupName {
cfg.APIPath = "/api"
}
gv := mapping.GroupVersionKind.GroupVersion()
cfg.ContentConfig = resource.UnstructuredPlusDefaultContentConfig()
cfg.GroupVersion = &gv
return restclient.RESTClientFor(cfg)
}
07070100000018000041ED00000000000000000000000266C10ABB00000000000000000000000000000000000000000000001B00000000kubectl-dup-0.3.2/pkg/util07070100000019000081A400000000000000000000000166C10ABB000005E2000000000000000000000000000000000000002300000000kubectl-dup-0.3.2/pkg/util/util.gopackage util
import (
"fmt"
"os"
"regexp"
"github.com/google/uuid"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
func GetDefaultNamespace(kubeconfig string) string {
defaultNamespace, _, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig},
&clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{}}).Namespace()
if err != nil {
fmt.Printf("Failed getting current namespace %v\n", err)
os.Exit(1)
}
return defaultNamespace
}
func GetDefaultKubeconfigPath() string {
homeDir, err := os.UserHomeDir()
if err != nil {
fmt.Println("Could not get home directory:%w", err)
}
kubeconfigDefaultPath := homeDir + "/.kube/config"
return kubeconfigDefaultPath
}
func GenerateResourceName(input string) string {
var result string
var suffix = "-dup-" + uuid.New().String()[:4]
var maxPodNameLength = 63 - len(suffix) //RFC1035
if len(input) > (maxPodNameLength) {
result = input[:maxPodNameLength] + suffix
} else {
result = input + suffix
}
return result
}
func IsValidPod(podName string) bool {
// Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names
return isValidDNSLabel(podName)
}
func isValidDNSLabel(input string) bool {
// Regular expression for DNS label validation as per RFC 1123
dnsLabelRegex := regexp.MustCompile(`^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$`)
return dnsLabelRegex.MatchString(input)
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!159 blocks