mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-04-15 07:16:34 +00:00
Cron job clean (#1862)
* created roadmap and yaml claude agent * Update roadmap.md * chore(deps): bump sigstore/cosign-installer from 3.9.2 to 3.10.0 (#1857) Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.9.2 to 3.10.0. - [Release notes](https://github.com/sigstore/cosign-installer/releases) - [Commits](https://github.com/sigstore/cosign-installer/compare/v3.9.2...v3.10.0) --- updated-dependencies: - dependency-name: sigstore/cosign-installer dependency-version: 3.10.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump the security group with 2 updates (#1858) Bumps the security group with 2 updates: [github.com/vmware-tanzu/velero](https://github.com/vmware-tanzu/velero) and [helm.sh/helm/v3](https://github.com/helm/helm). Updates `github.com/vmware-tanzu/velero` from 1.16.2 to 1.17.0 - [Release notes](https://github.com/vmware-tanzu/velero/releases) - [Changelog](https://github.com/vmware-tanzu/velero/blob/main/CHANGELOG.md) - [Commits](https://github.com/vmware-tanzu/velero/compare/v1.16.2...v1.17.0) Updates `helm.sh/helm/v3` from 3.18.6 to 3.19.0 - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.18.6...v3.19.0) --- updated-dependencies: - dependency-name: github.com/vmware-tanzu/velero dependency-version: 1.17.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: security - dependency-name: helm.sh/helm/v3 dependency-version: 3.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: security ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump helm.sh/helm/v3 from 3.18.6 to 3.19.0 in /examples/sdk/helm-template in the security group (#1859) chore(deps): bump helm.sh/helm/v3 Bumps the security group in /examples/sdk/helm-template with 1 update: [helm.sh/helm/v3](https://github.com/helm/helm). Updates `helm.sh/helm/v3` from 3.18.6 to 3.19.0 - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.18.6...v3.19.0) --- updated-dependencies: - dependency-name: helm.sh/helm/v3 dependency-version: 3.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: security ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add cron job support bundle scheduler Complete implementation with K8s integration: - pkg/schedule/job.go: Job management and persistence - pkg/schedule/daemon.go: Real-time scheduler daemon - pkg/schedule/cli.go: CLI commands (create, list, delete, daemon) - pkg/schedule/schedule_test.go: Comprehensive unit tests - cmd/troubleshoot/cli/root.go: CLI integration * fixing bugbot * Fix all bugbot errors: auto-update stability, job cooldown timing, and daemon execution * Deleting Agent * removed unused flags * fixing auto-upload * fixing markdown files * namespace not required flag for auto collectors to work * loosened cron job validation * writes logs to logfile * fix: resolve autoFromEnv variable scoping issue for CI - Ensure autoFromEnv variable and its usage are in correct scope - Fix build errors: declared and not used / undefined variable - All functionality preserved and tested locally - Force add to override gitignore --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Noah Campbell <noah.edward.campbell@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -48,3 +48,10 @@ sbom/
|
||||
# Ignore generated support bundles
|
||||
*.tar.gz
|
||||
!testdata/supportbundle/*.tar.gz
|
||||
|
||||
# Ignore built binaries
|
||||
troubleshoot
|
||||
troubleshoot-test
|
||||
cmd/troubleshoot/troubleshoot
|
||||
cmd/*/troubleshoot
|
||||
support-bundle
|
||||
1695
Cron-Job-Support-Bundles-PRD.md
Normal file
1695
Cron-Job-Support-Bundles-PRD.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -43,21 +43,25 @@ If no arguments are provided, specs are automatically loaded from the cluster by
|
||||
}
|
||||
|
||||
// Auto-update support-bundle unless disabled by flag or env
|
||||
envAuto := os.Getenv("TROUBLESHOOT_AUTO_UPDATE")
|
||||
autoFromEnv := true
|
||||
if envAuto != "" {
|
||||
if strings.EqualFold(envAuto, "0") || strings.EqualFold(envAuto, "false") {
|
||||
autoFromEnv = false
|
||||
// Only run auto-update for the root support-bundle command, not subcommands
|
||||
if cmd.Name() == "support-bundle" && !cmd.HasParent() {
|
||||
envAuto := os.Getenv("TROUBLESHOOT_AUTO_UPDATE")
|
||||
autoFromEnv := true
|
||||
if envAuto != "" {
|
||||
if strings.EqualFold(envAuto, "0") || strings.EqualFold(envAuto, "false") {
|
||||
autoFromEnv = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if v.GetBool("auto-update") && autoFromEnv {
|
||||
exe, err := os.Executable()
|
||||
if err == nil {
|
||||
_ = updater.CheckAndUpdate(cmd.Context(), updater.Options{
|
||||
BinaryName: "support-bundle",
|
||||
CurrentPath: exe,
|
||||
Printf: func(f string, a ...interface{}) { fmt.Fprintf(os.Stderr, f, a...) },
|
||||
})
|
||||
|
||||
if v.GetBool("auto-update") && autoFromEnv {
|
||||
exe, err := os.Executable()
|
||||
if err == nil {
|
||||
_ = updater.CheckAndUpdate(cmd.Context(), updater.Options{
|
||||
BinaryName: "support-bundle",
|
||||
CurrentPath: exe,
|
||||
Printf: func(f string, a ...interface{}) { fmt.Fprintf(os.Stderr, f, a...) },
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -103,11 +107,13 @@ If no arguments are provided, specs are automatically loaded from the cluster by
|
||||
cmd.AddCommand(Analyze())
|
||||
cmd.AddCommand(Redact())
|
||||
cmd.AddCommand(Diff())
|
||||
cmd.AddCommand(Schedule())
|
||||
cmd.AddCommand(UploadCmd())
|
||||
cmd.AddCommand(util.VersionCmd())
|
||||
|
||||
cmd.Flags().StringSlice("redactors", []string{}, "names of the additional redactors to use")
|
||||
cmd.Flags().Bool("redact", true, "enable/disable default redactions")
|
||||
|
||||
cmd.Flags().Bool("interactive", true, "enable/disable interactive mode")
|
||||
cmd.Flags().Bool("collect-without-permissions", true, "always generate a support bundle, even if it some require additional permissions")
|
||||
cmd.Flags().StringSliceP("selector", "l", []string{"troubleshoot.sh/kind=support-bundle"}, "selector to filter on for loading additional support bundle specs found in secrets within the cluster")
|
||||
|
||||
11
cmd/troubleshoot/cli/schedule.go
Normal file
11
cmd/troubleshoot/cli/schedule.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/replicatedhq/troubleshoot/pkg/schedule"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Schedule returns the schedule command for managing scheduled support bundle jobs
|
||||
func Schedule() *cobra.Command {
|
||||
return schedule.CLI()
|
||||
}
|
||||
@@ -9,7 +9,7 @@ replace github.com/replicatedhq/troubleshoot v0.0.0 => ../../../
|
||||
|
||||
require (
|
||||
github.com/replicatedhq/troubleshoot v0.0.0
|
||||
helm.sh/helm/v3 v3.18.6
|
||||
helm.sh/helm/v3 v3.19.0
|
||||
sigs.k8s.io/yaml v1.6.0
|
||||
)
|
||||
|
||||
@@ -17,20 +17,19 @@ require (
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // 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.1 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
@@ -40,35 +39,35 @@ require (
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.3 // indirect
|
||||
golang.org/x/crypto v0.41.0 // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/net v0.44.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/term v0.34.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/term v0.35.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // 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/api v0.33.4 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.33.4 // indirect
|
||||
k8s.io/apimachinery v0.33.4 // indirect
|
||||
k8s.io/client-go v0.33.4 // indirect
|
||||
k8s.io/api v0.34.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.34.1 // indirect
|
||||
k8s.io/apimachinery v0.34.1 // indirect
|
||||
k8s.io/client-go v0.34.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
sigs.k8s.io/controller-runtime v0.21.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
|
||||
sigs.k8s.io/controller-runtime v0.22.1 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
|
||||
)
|
||||
|
||||
@@ -6,8 +6,8 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
||||
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
@@ -18,12 +18,12 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
@@ -38,9 +38,8 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
|
||||
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
|
||||
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@@ -71,8 +70,9 @@ github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx
|
||||
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/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
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/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
|
||||
@@ -90,37 +90,37 @@ github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEV
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
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/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
|
||||
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
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/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
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.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -129,22 +129,22 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
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.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
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.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
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=
|
||||
@@ -162,31 +162,29 @@ 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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
helm.sh/helm/v3 v3.18.6 h1:S/2CqcYnNfLckkHLI0VgQbxgcDaU3N4A/46E3n9wSNY=
|
||||
helm.sh/helm/v3 v3.18.6/go.mod h1:L/dXDR2r539oPlFP1PJqKAC1CUgqHJDLkxKpDGrWnyg=
|
||||
k8s.io/api v0.33.4 h1:oTzrFVNPXBjMu0IlpA2eDDIU49jsuEorGHB4cvKupkk=
|
||||
k8s.io/api v0.33.4/go.mod h1:VHQZ4cuxQ9sCUMESJV5+Fe8bGnqAARZ08tSTdHWfeAc=
|
||||
k8s.io/apiextensions-apiserver v0.33.4 h1:rtq5SeXiDbXmSwxsF0MLe2Mtv3SwprA6wp+5qh/CrOU=
|
||||
k8s.io/apiextensions-apiserver v0.33.4/go.mod h1:mWXcZQkQV1GQyxeIjYApuqsn/081hhXPZwZ2URuJeSs=
|
||||
k8s.io/apimachinery v0.33.4 h1:SOf/JW33TP0eppJMkIgQ+L6atlDiP/090oaX0y9pd9s=
|
||||
k8s.io/apimachinery v0.33.4/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/client-go v0.33.4 h1:TNH+CSu8EmXfitntjUPwaKVPN0AYMbc9F1bBS8/ABpw=
|
||||
k8s.io/client-go v0.33.4/go.mod h1:LsA0+hBG2DPwovjd931L/AoaezMPX9CmBgyVyBZmbCY=
|
||||
helm.sh/helm/v3 v3.19.0 h1:krVyCGa8fa/wzTZgqw0DUiXuRT5BPdeqE/sQXujQ22k=
|
||||
helm.sh/helm/v3 v3.19.0/go.mod h1:Lk/SfzN0w3a3C3o+TdAKrLwJ0wcZ//t1/SDXAvfgDdc=
|
||||
k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
|
||||
k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk=
|
||||
k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI=
|
||||
k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc=
|
||||
k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
|
||||
k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
|
||||
k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY=
|
||||
k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8=
|
||||
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-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8=
|
||||
sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/controller-runtime v0.22.1 h1:Ah1T7I+0A7ize291nJZdS1CabF/lB4E++WizgV24Eqg=
|
||||
sigs.k8s.io/controller-runtime v0.22.1/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
|
||||
56
go.mod
56
go.mod
@@ -41,7 +41,7 @@ require (
|
||||
github.com/tj/go-spin v1.1.0
|
||||
github.com/vishvananda/netlink v1.3.1
|
||||
github.com/vishvananda/netns v0.0.5
|
||||
github.com/vmware-tanzu/velero v1.16.2
|
||||
github.com/vmware-tanzu/velero v1.17.0
|
||||
go.opentelemetry.io/otel v1.38.0
|
||||
go.opentelemetry.io/otel/sdk v1.38.0
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67
|
||||
@@ -63,19 +63,19 @@ require (
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.24.0 // indirect
|
||||
cloud.google.com/go/auth v0.14.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/monitoring v1.21.2 // indirect
|
||||
cloud.google.com/go/auth v0.16.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.7.0 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.2 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
|
||||
github.com/MakeNowJust/heredoc v1.0.0 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||
github.com/Masterminds/squirrel v1.5.4 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect
|
||||
@@ -97,7 +97,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.20 // indirect
|
||||
github.com/aws/smithy-go v1.22.3 // indirect
|
||||
github.com/chai2010/gettext-go v1.0.2 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
@@ -122,7 +122,7 @@ require (
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/google/go-containerregistry v0.20.3 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||
github.com/gosuri/uitable v0.0.4 // indirect
|
||||
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.65 // indirect
|
||||
@@ -159,29 +159,29 @@ require (
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/zeebo/errs v1.4.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
k8s.io/component-base v0.34.1 // indirect
|
||||
k8s.io/kubectl v0.33.3 // indirect
|
||||
k8s.io/kubectl v0.34.0 // indirect
|
||||
oras.land/oras-go/v2 v2.6.0 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.116.0 // indirect
|
||||
cloud.google.com/go/iam v1.2.2 // indirect
|
||||
cloud.google.com/go/storage v1.50.0 // indirect
|
||||
cloud.google.com/go v0.121.1 // indirect
|
||||
cloud.google.com/go/iam v1.5.2 // indirect
|
||||
cloud.google.com/go/storage v1.55.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
@@ -191,7 +191,7 @@ require (
|
||||
github.com/c9s/goprocinfo v0.0.0-20170724085704-0010a05ce49f // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/containerd/containerd v1.7.27 // indirect
|
||||
github.com/containerd/containerd v1.7.28 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
|
||||
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
|
||||
github.com/containers/ocicrypt v1.2.1 // indirect
|
||||
@@ -217,7 +217,7 @@ require (
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/go-intervals v0.0.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
@@ -252,7 +252,7 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/common v0.65.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
@@ -270,14 +270,14 @@ require (
|
||||
golang.org/x/sys v0.36.0
|
||||
golang.org/x/term v0.35.0 // indirect
|
||||
golang.org/x/text v0.29.0
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
google.golang.org/api v0.218.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
|
||||
google.golang.org/grpc v1.72.2 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
google.golang.org/api v0.241.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
helm.sh/helm/v3 v3.18.6
|
||||
helm.sh/helm/v3 v3.19.0
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
|
||||
k8s.io/kubelet v0.34.1
|
||||
k8s.io/metrics v0.34.1
|
||||
|
||||
136
go.sum
136
go.sum
@@ -1,34 +1,34 @@
|
||||
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
|
||||
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
|
||||
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
|
||||
cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM=
|
||||
cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
|
||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA=
|
||||
cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY=
|
||||
cloud.google.com/go/logging v1.12.0 h1:ex1igYcGFd4S/RZWOCU51StlIEuey5bjqwH9ZYjHibk=
|
||||
cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT7RejksUAM=
|
||||
cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc=
|
||||
cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
|
||||
cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU=
|
||||
cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU=
|
||||
cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs=
|
||||
cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY=
|
||||
cloud.google.com/go/trace v1.11.2 h1:4ZmaBdL8Ng/ajrgKqY5jfvzqMXbrDcBsUGXOT9aqTtI=
|
||||
cloud.google.com/go/trace v1.11.2/go.mod h1:bn7OwXd4pd5rFuAnTrzBuoZ4ax2XQeG3qNgYmfCy0Io=
|
||||
cloud.google.com/go v0.121.1 h1:S3kTQSydxmu1JfLRLpKtxRPA7rSrYPRPEUmL/PavVUw=
|
||||
cloud.google.com/go v0.121.1/go.mod h1:nRFlrHq39MNVWu+zESP2PosMWA0ryJw8KUBZ2iZpxbw=
|
||||
cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
|
||||
cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
|
||||
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
|
||||
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
|
||||
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
|
||||
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
|
||||
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
|
||||
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
|
||||
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
|
||||
cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM=
|
||||
cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U=
|
||||
cloud.google.com/go/storage v1.55.0 h1:NESjdAToN9u1tmhVqhXCaCwYBuvEhZLLv0gBr+2znf0=
|
||||
cloud.google.com/go/storage v1.55.0/go.mod h1:ztSmTTwzsdXe5syLVS0YsbFxXuvEmEyZj7v7zChEmuY=
|
||||
cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4=
|
||||
cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 h1:Wc1ml6QlJs2BHQ/9Bqu1jiyggbsSjramq2oUmp5WeIo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
|
||||
@@ -46,20 +46,20 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 h1:f2Qw/Ehhimh5uO1fayV0QIW7DShEQqhtUfhYc+cBPlw=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0/go.mod h1:2bIszWvQRlJVmJLiuLhukLImRjKPcYdzzsx6darK02A=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 h1:UQ0AhxogsIRZDkElkblfnwjc3IaltCm2HUMvezQaL7s=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.1 h1:oTX4vsorBZo/Zdum6OKPA4o7544hm6smoRv1QjpTwGo=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.1/go.mod h1:0wEl7vrAD8mehJyohS9HZy+WyEOaQO2mJx86Cvh93kM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 h1:8nn+rsCvTq9axyEh382S0PFLBeaFwNsT43IrPWzctRU=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1/go.mod h1:viRWSEhtMZqz1rhwmOVKkWl6SwmVowfL9O2YR5gI2PE=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
|
||||
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
||||
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
||||
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
|
||||
@@ -148,12 +148,12 @@ github.com/cilium/ebpf v0.19.0 h1:Ro/rE64RmFBeA9FGjcTc+KmCeY6jXmryu6FfnzPRIao=
|
||||
github.com/cilium/ebpf v0.19.0/go.mod h1:fLCgMo3l8tZmAdM3B2XqdFzXBpwkcSTroaVqN08OWVY=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk=
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k=
|
||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
|
||||
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
|
||||
github.com/containerd/containerd v1.7.27 h1:yFyEyojddO3MIGVER2xJLWoCIn+Up4GaHFquP7hsFII=
|
||||
github.com/containerd/containerd v1.7.27/go.mod h1:xZmPnl75Vc+BLGt4MIfu6bp+fy03gdHAn9bz+FreFR0=
|
||||
github.com/containerd/containerd v1.7.28 h1:Nsgm1AtcmEh4AHAJ4gGlNSaKgXiNccU270Dnf81FQ3c=
|
||||
github.com/containerd/containerd v1.7.28/go.mod h1:azUkWcOvHrWvaiUjSQH0fjzuHIwSPg1WL5PshGP4Szs=
|
||||
github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII=
|
||||
github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
@@ -345,10 +345,10 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0=
|
||||
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w=
|
||||
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
|
||||
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
@@ -559,8 +559,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
@@ -653,8 +653,8 @@ github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zd
|
||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/vladimirvivien/gexe v0.4.1 h1:W9gWkp8vSPjDoXDu04Yp4KljpVMaSt8IQuHswLDd5LY=
|
||||
github.com/vladimirvivien/gexe v0.4.1/go.mod h1:3gjgTqE2c0VyHnU5UOIwk7gyNzZDGulPb/DJPgcw64E=
|
||||
github.com/vmware-tanzu/velero v1.16.2 h1:Zhve1mKtX4n0oVhHwbEOsgB9fjKKwm96HJK4WaV/28o=
|
||||
github.com/vmware-tanzu/velero v1.16.2/go.mod h1:rGIxqbeVHne/47AMtA8vV0ebeQOzyF7VEullayyTEto=
|
||||
github.com/vmware-tanzu/velero v1.17.0 h1:b+KLlBG+v1YKogP81nAFix2pgJBTmUrnVlXg+OfB5ao=
|
||||
github.com/vmware-tanzu/velero v1.17.0/go.mod h1:BJRFKei89hSqrazQKiwv5YhhX871X1W1qPyo5OP09zw=
|
||||
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=
|
||||
@@ -671,14 +671,14 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 h1:UW0+QyeyBVhn+COBec3nGhfnFe5lwB0ic1JBVjzhk0w=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.57.0/go.mod h1:ppciCHRLsyCio54qbzQv0E4Jyth/fLWDTJYfvWpcSVk=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.36.0 h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 h1:jmTVJ86dP60C01K3slFQa2NQ/Aoi7zA+wy7vMOKD9H4=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.57.0/go.mod h1:EJBheUMttD/lABFyLXhce47Wr6DPWYReCzaZiXadH7g=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls=
|
||||
@@ -699,8 +699,8 @@ go.opentelemetry.io/otel/exporters/prometheus v0.54.0 h1:rFwzp68QMgtzu9PgP3jm9Xa
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.54.0/go.mod h1:QyjcV9qDP6VeK5qPyKETvNjmaaEc7+gqjh4SS0ZYzDU=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 h1:CHXNXwfKWfzS65yrlB2PVds1IBZcdsX8Vepy9of0iRU=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0/go.mod h1:zKU4zUgKiaRxrdovSS2amdM5gOc59slmo/zJwGX+YBg=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 h1:SZmDnHcgp3zwlPBS2JX2urGYe/jBKEIT6ZedHRUyCz8=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0/go.mod h1:fdWW0HtZJ7+jNpTKUR0GpMEDP69nR8YBJQxNiVCE3jk=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 h1:cC2yDI3IQd0Udsux7Qmq8ToKAx1XCilTQECZ0KDZyTw=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0/go.mod h1:2PD5Ex6z8CFzDbTdOlwyNIUywRr1DN0ospafJM1wJ+s=
|
||||
go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk=
|
||||
@@ -797,8 +797,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -813,26 +813,26 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
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/api v0.218.0 h1:x6JCjEWeZ9PFCRe9z0FBrNwj7pB7DOAqT35N+IPnAUA=
|
||||
google.golang.org/api v0.218.0/go.mod h1:5VGHBAkxrA/8EFjLVEYmMUJ8/8+gWWQ3s4cFH0FxG2M=
|
||||
google.golang.org/api v0.241.0 h1:QKwqWQlkc6O895LchPEDUSYr22Xp3NCxpQRiWTB6avE=
|
||||
google.golang.org/api v0.241.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
|
||||
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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
|
||||
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -867,8 +867,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
helm.sh/helm/v3 v3.18.6 h1:S/2CqcYnNfLckkHLI0VgQbxgcDaU3N4A/46E3n9wSNY=
|
||||
helm.sh/helm/v3 v3.18.6/go.mod h1:L/dXDR2r539oPlFP1PJqKAC1CUgqHJDLkxKpDGrWnyg=
|
||||
helm.sh/helm/v3 v3.19.0 h1:krVyCGa8fa/wzTZgqw0DUiXuRT5BPdeqE/sQXujQ22k=
|
||||
helm.sh/helm/v3 v3.19.0/go.mod h1:Lk/SfzN0w3a3C3o+TdAKrLwJ0wcZ//t1/SDXAvfgDdc=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
|
||||
@@ -889,8 +889,8 @@ 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-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
||||
k8s.io/kubectl v0.33.3 h1:r/phHvH1iU7gO/l7tTjQk2K01ER7/OAJi8uFHHyWSac=
|
||||
k8s.io/kubectl v0.33.3/go.mod h1:euj2bG56L6kUGOE/ckZbCoudPwuj4Kud7BR0GzyNiT0=
|
||||
k8s.io/kubectl v0.34.0 h1:NcXz4TPTaUwhiX4LU+6r6udrlm0NsVnSkP3R9t0dmxs=
|
||||
k8s.io/kubectl v0.34.0/go.mod h1:bmd0W5i+HuG7/p5sqicr0Li0rR2iIhXL0oUyLF3OjR4=
|
||||
k8s.io/kubelet v0.34.1 h1:doAaTA9/Yfzbdq/u/LveZeONp96CwX9giW6b+oHn4m4=
|
||||
k8s.io/kubelet v0.34.1/go.mod h1:PtV3Ese8iOM19gSooFoQT9iyRisbmJdAPuDImuccbbA=
|
||||
k8s.io/kubernetes v1.34.1 h1:F3p8dtpv+i8zQoebZeK5zBqM1g9x1aIdnA5vthvcuUk=
|
||||
|
||||
172
pkg/schedule/cli.go
Normal file
172
pkg/schedule/cli.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package schedule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// CLI creates the schedule command
|
||||
func CLI() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "schedule",
|
||||
Short: "Manage scheduled support bundle jobs",
|
||||
Long: `Create and manage scheduled support bundle collection jobs.
|
||||
|
||||
This allows customers to schedule support bundle collection to run automatically
|
||||
at specified times using standard cron syntax.`,
|
||||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
createCommand(),
|
||||
listCommand(),
|
||||
deleteCommand(),
|
||||
daemonCommand(),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// createCommand creates the create subcommand
|
||||
func createCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "create [job-name] --cron [schedule] [--namespace ns]",
|
||||
Short: "Create a scheduled support bundle job",
|
||||
Long: `Create a new scheduled job to automatically collect support bundles.
|
||||
|
||||
Examples:
|
||||
# Daily at 2 AM
|
||||
support-bundle schedule create daily-check --cron "0 2 * * *" --namespace production
|
||||
|
||||
# Every 6 hours with auto-discovery and auto-upload to vendor portal
|
||||
support-bundle schedule create frequent --cron "0 */6 * * *" --namespace app --auto --upload enabled`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cronSchedule, _ := cmd.Flags().GetString("cron")
|
||||
namespace, _ := cmd.Flags().GetString("namespace")
|
||||
auto, _ := cmd.Flags().GetBool("auto")
|
||||
upload, _ := cmd.Flags().GetString("upload")
|
||||
|
||||
if cronSchedule == "" {
|
||||
return fmt.Errorf("--cron is required")
|
||||
}
|
||||
|
||||
manager, err := NewManager()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
job, err := manager.CreateJob(args[0], cronSchedule, namespace, auto, upload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Created scheduled job '%s' (ID: %s)\n", job.Name, job.ID)
|
||||
fmt.Printf(" Schedule: %s\n", job.Schedule)
|
||||
fmt.Printf(" Namespace: %s\n", job.Namespace)
|
||||
if auto {
|
||||
fmt.Printf(" Auto-discovery: enabled\n")
|
||||
}
|
||||
if upload != "" {
|
||||
fmt.Printf(" Auto-upload: enabled (uploads to vendor portal)\n")
|
||||
}
|
||||
|
||||
fmt.Printf("\n💡 To activate, start the daemon:\n")
|
||||
fmt.Printf(" support-bundle schedule daemon start\n")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringP("cron", "c", "", "Cron expression (required)")
|
||||
cmd.Flags().StringP("namespace", "n", "", "Kubernetes namespace (optional)")
|
||||
cmd.Flags().Bool("auto", false, "Enable auto-discovery")
|
||||
cmd.Flags().String("upload", "", "Enable auto-upload to vendor portal (any non-empty value enables auto-upload)")
|
||||
cmd.MarkFlagRequired("cron")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// listCommand creates the list subcommand
|
||||
func listCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List all scheduled jobs",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
manager, err := NewManager()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
jobs, err := manager.ListJobs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(jobs) == 0 {
|
||||
fmt.Println("No scheduled jobs found")
|
||||
return nil
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
|
||||
fmt.Fprintln(w, "NAME\tSCHEDULE\tNAMESPACE\tAUTO\tAUTO-UPLOAD\tRUNS")
|
||||
|
||||
for _, job := range jobs {
|
||||
upload := "none"
|
||||
if job.Upload != "" {
|
||||
upload = "enabled"
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%t\t%s\t%d\n",
|
||||
job.Name, job.Schedule, job.Namespace, job.Auto, upload, job.RunCount)
|
||||
}
|
||||
|
||||
return w.Flush()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// deleteCommand creates the delete subcommand
|
||||
func deleteCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "delete [job-name]",
|
||||
Short: "Delete a scheduled job",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
manager, err := NewManager()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := manager.DeleteJob(args[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Deleted job: %s\n", args[0])
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// daemonCommand creates the daemon subcommand
|
||||
func daemonCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "daemon",
|
||||
Short: "Manage scheduler daemon",
|
||||
}
|
||||
|
||||
start := &cobra.Command{
|
||||
Use: "start",
|
||||
Short: "Start the scheduler daemon",
|
||||
Long: "Start the daemon to automatically execute scheduled jobs",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
daemon, err := NewDaemon()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return daemon.Start()
|
||||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(start)
|
||||
return cmd
|
||||
}
|
||||
342
pkg/schedule/daemon.go
Normal file
342
pkg/schedule/daemon.go
Normal file
@@ -0,0 +1,342 @@
|
||||
package schedule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Daemon runs scheduled jobs
|
||||
type Daemon struct {
|
||||
manager *Manager
|
||||
running bool
|
||||
jobMutex sync.Mutex
|
||||
runningJobs map[string]bool // Track running jobs to prevent concurrent execution
|
||||
logger *log.Logger
|
||||
logFile *os.File
|
||||
}
|
||||
|
||||
// NewDaemon creates a new daemon
|
||||
func NewDaemon() (*Daemon, error) {
|
||||
manager, err := NewManager()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create job manager: %w", err)
|
||||
}
|
||||
|
||||
// Setup persistent logging
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get user home directory: %w", err)
|
||||
}
|
||||
|
||||
logDir := filepath.Join(homeDir, ".troubleshoot")
|
||||
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||||
return nil, fmt.Errorf("failed to create log directory %s: %w", logDir, err)
|
||||
}
|
||||
|
||||
logPath := filepath.Join(logDir, "scheduler.log")
|
||||
logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open log file %s: %w", logPath, err)
|
||||
}
|
||||
|
||||
logger := log.New(logFile, "", log.LstdFlags)
|
||||
|
||||
return &Daemon{
|
||||
manager: manager,
|
||||
running: false,
|
||||
runningJobs: make(map[string]bool),
|
||||
logger: logger,
|
||||
logFile: logFile,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Start starts the daemon to monitor and execute jobs
|
||||
func (d *Daemon) Start() error {
|
||||
d.running = true
|
||||
|
||||
// Setup signal handling
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
// Ensure signal handling is cleaned up and close log file
|
||||
defer func() {
|
||||
signal.Stop(sigChan)
|
||||
if d.logFile != nil {
|
||||
d.logFile.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
d.logInfo("Scheduler daemon started")
|
||||
d.logInfo("Monitoring scheduled jobs every minute...")
|
||||
|
||||
ticker := time.NewTicker(1 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for d.running {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
d.checkAndExecuteJobs()
|
||||
case sig := <-sigChan:
|
||||
d.logInfo(fmt.Sprintf("Received signal %v, shutting down...", sig))
|
||||
d.running = false
|
||||
}
|
||||
}
|
||||
|
||||
d.logInfo("Scheduler daemon stopped")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the daemon
|
||||
func (d *Daemon) Stop() {
|
||||
d.running = false
|
||||
}
|
||||
|
||||
// checkAndExecuteJobs checks for jobs that should run now
|
||||
func (d *Daemon) checkAndExecuteJobs() {
|
||||
jobs, err := d.manager.ListJobs()
|
||||
if err != nil {
|
||||
d.logError(fmt.Sprintf("Error loading jobs: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
for _, job := range jobs {
|
||||
if job == nil {
|
||||
continue // Skip nil jobs
|
||||
}
|
||||
|
||||
if job.Enabled && d.shouldJobRun(job, now) {
|
||||
// Check if job is already running to prevent concurrent execution
|
||||
d.jobMutex.Lock()
|
||||
if d.runningJobs[job.ID] {
|
||||
d.jobMutex.Unlock()
|
||||
continue // Skip if already running
|
||||
}
|
||||
d.runningJobs[job.ID] = true
|
||||
d.jobMutex.Unlock()
|
||||
|
||||
go d.executeJob(job)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// shouldJobRun checks if a job should run based on its schedule
|
||||
func (d *Daemon) shouldJobRun(job *Job, now time.Time) bool {
|
||||
if job == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Prevent running multiple times in the same minute (avoid duplicates)
|
||||
// Use 90-second cooldown to ensure we don't run more than once per minute
|
||||
// even with slight timing variations in the daemon's check cycle
|
||||
if !job.LastRun.IsZero() && now.Sub(job.LastRun) < 90*time.Second {
|
||||
return false
|
||||
}
|
||||
|
||||
// Parse cron schedule (minute hour day-of-month month day-of-week)
|
||||
parts := strings.Fields(job.Schedule)
|
||||
if len(parts) != 5 {
|
||||
return false
|
||||
}
|
||||
|
||||
minute := parts[0]
|
||||
hour := parts[1]
|
||||
dayOfMonth := parts[2]
|
||||
month := parts[3]
|
||||
dayOfWeek := parts[4]
|
||||
|
||||
// Check if current time matches all cron fields
|
||||
if !matchesCronField(minute, now.Minute()) {
|
||||
return false
|
||||
}
|
||||
if !matchesCronField(hour, now.Hour()) {
|
||||
return false
|
||||
}
|
||||
if !matchesCronField(dayOfMonth, now.Day()) {
|
||||
return false
|
||||
}
|
||||
if !matchesCronField(month, int(now.Month())) {
|
||||
return false
|
||||
}
|
||||
// Day of week: Sunday = 0, Monday = 1, etc.
|
||||
if !matchesCronField(dayOfWeek, int(now.Weekday())) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// matchesCronField checks if a cron field matches the current time value
|
||||
func matchesCronField(field string, currentValue int) bool {
|
||||
if field == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Handle */N syntax (e.g., */2 for every 2 minutes)
|
||||
if strings.HasPrefix(field, "*/") {
|
||||
intervalStr := strings.TrimPrefix(field, "*/")
|
||||
if interval, err := strconv.Atoi(intervalStr); err == nil && interval > 0 {
|
||||
return currentValue%interval == 0
|
||||
}
|
||||
return false // Invalid interval format
|
||||
}
|
||||
|
||||
// Handle comma-separated lists (e.g., "1,15,30")
|
||||
values := strings.Split(field, ",")
|
||||
for _, val := range values {
|
||||
val = strings.TrimSpace(val)
|
||||
if fieldValue, err := strconv.Atoi(val); err == nil {
|
||||
if currentValue == fieldValue {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// findSupportBundleBinary finds the support-bundle binary path
|
||||
func findSupportBundleBinary() (string, error) {
|
||||
// First try current directory
|
||||
if _, err := os.Stat("./support-bundle"); err == nil {
|
||||
abs, _ := filepath.Abs("./support-bundle")
|
||||
return abs, nil
|
||||
}
|
||||
|
||||
// Try relative to current binary location
|
||||
if execPath, err := os.Executable(); err == nil {
|
||||
supportBundlePath := filepath.Join(filepath.Dir(execPath), "support-bundle")
|
||||
if _, err := os.Stat(supportBundlePath); err == nil {
|
||||
return supportBundlePath, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Try PATH
|
||||
if path, err := exec.LookPath("support-bundle"); err == nil {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("support-bundle binary not found")
|
||||
}
|
||||
|
||||
// executeJob runs a support bundle collection
|
||||
func (d *Daemon) executeJob(job *Job) {
|
||||
if job == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure we mark the job as not running when done
|
||||
defer func() {
|
||||
d.jobMutex.Lock()
|
||||
delete(d.runningJobs, job.ID)
|
||||
d.jobMutex.Unlock()
|
||||
}()
|
||||
|
||||
d.logInfo(fmt.Sprintf("Executing job: %s", job.Name))
|
||||
|
||||
// Build command arguments (no subcommand needed - binary IS support-bundle)
|
||||
args := []string{}
|
||||
if job.Namespace != "" {
|
||||
args = append(args, "--namespace", job.Namespace)
|
||||
}
|
||||
if job.Auto {
|
||||
args = append(args, "--auto")
|
||||
}
|
||||
if job.Upload != "" {
|
||||
args = append(args, "--auto-upload")
|
||||
// Add license and app flags if available in the future
|
||||
// if job.LicenseID != "" {
|
||||
// args = append(args, "--license-id", job.LicenseID)
|
||||
// }
|
||||
// if job.AppSlug != "" {
|
||||
// args = append(args, "--app-slug", job.AppSlug)
|
||||
// }
|
||||
}
|
||||
|
||||
// Disable auto-update for scheduled jobs
|
||||
args = append(args, "--auto-update=false")
|
||||
|
||||
// Find support-bundle binary
|
||||
supportBundleBinary, err := findSupportBundleBinary()
|
||||
if err != nil {
|
||||
d.logError(fmt.Sprintf("Job failed: %s - cannot find support-bundle binary: %v", job.Name, err))
|
||||
return
|
||||
}
|
||||
|
||||
// Execute support-bundle command directly with output capture
|
||||
cmd := exec.Command(supportBundleBinary, args...)
|
||||
|
||||
// Capture both stdout and stderr
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
if err != nil {
|
||||
d.logError(fmt.Sprintf("Job failed: %s - %v", job.Name, err))
|
||||
if len(output) > 0 {
|
||||
d.logError(fmt.Sprintf("Command output for %s:\n%s", job.Name, string(output)))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
d.logInfo(fmt.Sprintf("Job completed: %s", job.Name))
|
||||
|
||||
// Log key information but skip verbose JSON output
|
||||
if len(output) > 0 {
|
||||
outputStr := string(output)
|
||||
|
||||
// Extract and log only the important parts
|
||||
if strings.Contains(outputStr, "Successfully uploaded support bundle") {
|
||||
d.logInfo(fmt.Sprintf("Upload successful for job: %s", job.Name))
|
||||
}
|
||||
if strings.Contains(outputStr, "Auto-upload failed:") {
|
||||
// Log upload failures in detail
|
||||
lines := strings.Split(outputStr, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "Auto-upload failed:") {
|
||||
d.logError(fmt.Sprintf("Upload failed for job %s: %s", job.Name, strings.TrimSpace(line)))
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.Contains(outputStr, "archivePath") {
|
||||
// Extract just the archive name
|
||||
lines := strings.Split(outputStr, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "archivePath") {
|
||||
d.logInfo(fmt.Sprintf("Archive created for job %s: %s", job.Name, strings.TrimSpace(line)))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update job stats only on success
|
||||
job.RunCount++
|
||||
job.LastRun = time.Now()
|
||||
if err := d.manager.saveJob(job); err != nil {
|
||||
d.logError(fmt.Sprintf("Warning: Failed to save job statistics for %s: %v", job.Name, err))
|
||||
}
|
||||
}
|
||||
|
||||
// logInfo logs an info message to both console and file
|
||||
func (d *Daemon) logInfo(message string) {
|
||||
fmt.Printf("✓ %s\n", message)
|
||||
if d.logger != nil {
|
||||
d.logger.Printf("INFO: %s", message)
|
||||
}
|
||||
}
|
||||
|
||||
// logError logs an error message to both console and file
|
||||
func (d *Daemon) logError(message string) {
|
||||
fmt.Printf("❌ %s\n", message)
|
||||
if d.logger != nil {
|
||||
d.logger.Printf("ERROR: %s", message)
|
||||
}
|
||||
}
|
||||
212
pkg/schedule/job.go
Normal file
212
pkg/schedule/job.go
Normal file
@@ -0,0 +1,212 @@
|
||||
package schedule
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Job represents a scheduled support bundle collection job
|
||||
type Job struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Schedule string `json:"schedule"` // Cron expression
|
||||
Namespace string `json:"namespace"`
|
||||
Auto bool `json:"auto"` // Auto-discovery
|
||||
Upload string `json:"upload,omitempty"`
|
||||
Enabled bool `json:"enabled"`
|
||||
RunCount int `json:"runCount"`
|
||||
LastRun time.Time `json:"lastRun,omitempty"`
|
||||
Created time.Time `json:"created"`
|
||||
}
|
||||
|
||||
// Manager handles job operations
|
||||
type Manager struct {
|
||||
storageDir string
|
||||
}
|
||||
|
||||
// NewManager creates a new job manager
|
||||
func NewManager() (*Manager, error) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get user home directory: %w", err)
|
||||
}
|
||||
|
||||
storageDir := filepath.Join(homeDir, ".troubleshoot", "scheduled-jobs")
|
||||
if err := os.MkdirAll(storageDir, 0755); err != nil {
|
||||
return nil, fmt.Errorf("failed to create storage directory %s: %w", storageDir, err)
|
||||
}
|
||||
|
||||
return &Manager{storageDir: storageDir}, nil
|
||||
}
|
||||
|
||||
// CreateJob creates a new scheduled job
|
||||
func (m *Manager) CreateJob(name, schedule, namespace string, auto bool, upload string) (*Job, error) {
|
||||
// Input validation
|
||||
if strings.TrimSpace(name) == "" {
|
||||
return nil, fmt.Errorf("job name cannot be empty")
|
||||
}
|
||||
|
||||
// Sanitize job name for filesystem safety
|
||||
name = strings.TrimSpace(name)
|
||||
if len(name) > 100 {
|
||||
return nil, fmt.Errorf("job name too long, maximum 100 characters")
|
||||
}
|
||||
|
||||
// Check for invalid filename characters
|
||||
invalidChars := []string{"/", "\\", ":", "*", "?", "\"", "<", ">", "|", "\x00"}
|
||||
for _, char := range invalidChars {
|
||||
if strings.Contains(name, char) {
|
||||
return nil, fmt.Errorf("job name contains invalid character: %s", char)
|
||||
}
|
||||
}
|
||||
|
||||
// Cron validation - check it has 5 parts and basic field validation
|
||||
if err := validateCronSchedule(schedule); err != nil {
|
||||
return nil, fmt.Errorf("invalid cron schedule: %w", err)
|
||||
}
|
||||
|
||||
job := &Job{
|
||||
ID: generateJobID(),
|
||||
Name: name,
|
||||
Schedule: schedule,
|
||||
Namespace: namespace,
|
||||
Auto: auto,
|
||||
Upload: upload,
|
||||
Enabled: true,
|
||||
Created: time.Now(),
|
||||
}
|
||||
|
||||
if err := m.saveJob(job); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return job, nil
|
||||
}
|
||||
|
||||
// ListJobs returns all saved jobs
|
||||
func (m *Manager) ListJobs() ([]*Job, error) {
|
||||
files, err := filepath.Glob(filepath.Join(m.storageDir, "*.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var jobs []*Job
|
||||
for _, file := range files {
|
||||
job, err := m.loadJobFromFile(file)
|
||||
if err != nil {
|
||||
continue // Skip invalid files
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
}
|
||||
|
||||
return jobs, nil
|
||||
}
|
||||
|
||||
// GetJob retrieves a job by name or ID
|
||||
func (m *Manager) GetJob(nameOrID string) (*Job, error) {
|
||||
jobs, err := m.ListJobs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, job := range jobs {
|
||||
if job.Name == nameOrID || job.ID == nameOrID {
|
||||
return job, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("job not found: %s", nameOrID)
|
||||
}
|
||||
|
||||
// DeleteJob removes a job
|
||||
func (m *Manager) DeleteJob(nameOrID string) error {
|
||||
job, err := m.GetJob(nameOrID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jobFile := filepath.Join(m.storageDir, job.ID+".json")
|
||||
return os.Remove(jobFile)
|
||||
}
|
||||
|
||||
// saveJob saves a job to a JSON file
|
||||
func (m *Manager) saveJob(job *Job) error {
|
||||
data, err := json.MarshalIndent(job, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jobFile := filepath.Join(m.storageDir, job.ID+".json")
|
||||
return os.WriteFile(jobFile, data, 0644)
|
||||
}
|
||||
|
||||
// loadJobFromFile loads a job from a JSON file
|
||||
func (m *Manager) loadJobFromFile(filename string) (*Job, error) {
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var job Job
|
||||
err = json.Unmarshal(data, &job)
|
||||
return &job, err
|
||||
}
|
||||
|
||||
// validateCronSchedule performs basic cron schedule validation
|
||||
func validateCronSchedule(schedule string) error {
|
||||
parts := strings.Fields(schedule)
|
||||
if len(parts) != 5 {
|
||||
return fmt.Errorf("expected 5 fields (minute hour day-of-month month day-of-week), got %d", len(parts))
|
||||
}
|
||||
|
||||
// Validate each field has reasonable values
|
||||
fieldNames := []string{"minute", "hour", "day-of-month", "month", "day-of-week"}
|
||||
fieldRanges := [][2]int{{0, 59}, {0, 23}, {1, 31}, {1, 12}, {0, 6}}
|
||||
|
||||
for i, field := range parts {
|
||||
if err := validateCronField(field, fieldRanges[i][0], fieldRanges[i][1], fieldNames[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateCronField validates a single cron field
|
||||
func validateCronField(field string, min, max int, fieldName string) error {
|
||||
if field == "*" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle */N syntax
|
||||
if strings.HasPrefix(field, "*/") {
|
||||
intervalStr := strings.TrimPrefix(field, "*/")
|
||||
if interval, err := strconv.Atoi(intervalStr); err != nil || interval <= 0 {
|
||||
return fmt.Errorf("invalid %s interval: %s", fieldName, intervalStr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle exact values (including comma-separated lists)
|
||||
values := strings.Split(field, ",")
|
||||
for _, val := range values {
|
||||
val = strings.TrimSpace(val)
|
||||
if fieldValue, err := strconv.Atoi(val); err != nil {
|
||||
return fmt.Errorf("invalid %s value: %s", fieldName, val)
|
||||
} else if fieldValue < min || fieldValue > max {
|
||||
return fmt.Errorf("%s value %d out of range [%d-%d]", fieldName, fieldValue, min, max)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateJobID generates a simple job ID
|
||||
func generateJobID() string {
|
||||
return fmt.Sprintf("job-%d", time.Now().UnixNano())
|
||||
}
|
||||
124
pkg/schedule/schedule_test.go
Normal file
124
pkg/schedule/schedule_test.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package schedule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestManager_CreateJob(t *testing.T) {
|
||||
// Use temporary directory for testing
|
||||
tempDir, err := os.MkdirTemp("", "schedule-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
manager := &Manager{storageDir: tempDir}
|
||||
|
||||
// Test job creation
|
||||
job, err := manager.CreateJob("test-job", "0 2 * * *", "default", true, "s3://bucket")
|
||||
if err != nil {
|
||||
t.Fatalf("CreateJob failed: %v", err)
|
||||
}
|
||||
|
||||
if job.Name != "test-job" {
|
||||
t.Errorf("Job name = %s, want test-job", job.Name)
|
||||
}
|
||||
|
||||
if job.Schedule != "0 2 * * *" {
|
||||
t.Errorf("Schedule = %s, want 0 2 * * *", job.Schedule)
|
||||
}
|
||||
|
||||
if !job.Enabled {
|
||||
t.Error("Job should be enabled by default")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManager_ListJobs(t *testing.T) {
|
||||
tempDir, err := os.MkdirTemp("", "schedule-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
manager := &Manager{storageDir: tempDir}
|
||||
|
||||
// Create test jobs
|
||||
_, err = manager.CreateJob("job1", "0 1 * * *", "ns1", false, "")
|
||||
if err != nil {
|
||||
t.Fatalf("CreateJob failed: %v", err)
|
||||
}
|
||||
|
||||
_, err = manager.CreateJob("job2", "0 2 * * *", "ns2", true, "s3://bucket")
|
||||
if err != nil {
|
||||
t.Fatalf("CreateJob failed: %v", err)
|
||||
}
|
||||
|
||||
// List jobs
|
||||
jobs, err := manager.ListJobs()
|
||||
if err != nil {
|
||||
t.Fatalf("ListJobs failed: %v", err)
|
||||
}
|
||||
|
||||
if len(jobs) != 2 {
|
||||
t.Errorf("Expected 2 jobs, got %d", len(jobs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestManager_DeleteJob(t *testing.T) {
|
||||
tempDir, err := os.MkdirTemp("", "schedule-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
manager := &Manager{storageDir: tempDir}
|
||||
|
||||
// Create and delete job
|
||||
job, err := manager.CreateJob("temp-job", "0 3 * * *", "default", false, "")
|
||||
if err != nil {
|
||||
t.Fatalf("CreateJob failed: %v", err)
|
||||
}
|
||||
|
||||
err = manager.DeleteJob(job.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("DeleteJob failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify deletion
|
||||
jobs, err := manager.ListJobs()
|
||||
if err != nil {
|
||||
t.Fatalf("ListJobs failed: %v", err)
|
||||
}
|
||||
|
||||
if len(jobs) != 0 {
|
||||
t.Errorf("Expected 0 jobs after deletion, got %d", len(jobs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDaemon_ScheduleMatching(t *testing.T) {
|
||||
daemon, err := NewDaemon()
|
||||
if err != nil {
|
||||
t.Fatalf("NewDaemon failed: %v", err)
|
||||
}
|
||||
|
||||
// Test job that should run at current minute
|
||||
now := time.Now()
|
||||
job := &Job{
|
||||
Schedule: fmt.Sprintf("%d %d * * *", now.Minute(), now.Hour()),
|
||||
LastRun: time.Time{}, // Never run
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
if !daemon.shouldJobRun(job, now) {
|
||||
t.Error("Job should run at current time")
|
||||
}
|
||||
|
||||
// Test job that just ran
|
||||
job.LastRun = now.Add(-25 * time.Second)
|
||||
if daemon.shouldJobRun(job, now) {
|
||||
t.Error("Job should not run again so soon")
|
||||
}
|
||||
}
|
||||
621
roadmap.md
Normal file
621
roadmap.md
Normal file
@@ -0,0 +1,621 @@
|
||||
### Phased execution plan (actionable)
|
||||
|
||||
1) Foundation & policy (cross-cutting)
|
||||
• Goal: Establish non-negotiable engineering charters, error taxonomy, deterministic I/O, and output envelope.
|
||||
• Do:
|
||||
• Adopt items under “Cross-cutting engineering charters”.
|
||||
• Implement centralized error codes (see “1) Error codes (centralized)”).
|
||||
• Implement JSON output envelope (see “2) Output envelope (JSON mode)”).
|
||||
• Add idempotency key helper (see “3) Idempotency key”).
|
||||
• Ensure deterministic marshaling patterns (see “4) Deterministic marshaling”).
|
||||
• Define config precedence and env aliases (see section E) Config precedence & env aliases).
|
||||
• Add Make targets (see section F) Make targets).
|
||||
• Acceptance:
|
||||
• “Measurable add-on success criteria” items related to CLI output and determinism are satisfied.
|
||||
|
||||
2) Distribution & updates (installers, signing, updater)
|
||||
• Goal: Stop krew; ship Homebrew and curl|bash installers; add secure update with rollback.
|
||||
• Do:
|
||||
• Remove/retire krew guidance; add Homebrew formulas and curl|bash script(s).
|
||||
• Implement “C) Update system (secure + rollback)” including channels, rollback, tamper defense, delta updates (optional later).
|
||||
• Implement “Reproducible, signed, attestable releases” (SBOM, cosign, SLSA, SOURCE_DATE_EPOCH).
|
||||
• Add minimal packaging matrix validation for brew and curl|bash; expand later (see D) Packaging matrix validation (CI)).
|
||||
• Acceptance:
|
||||
• Users can install preflight and support-bundle via brew and curl|bash.
|
||||
• Updater supports --channel, verify, rollback; signatures verified per roadmap details.
|
||||
|
||||
3) API v1beta3 schemas and libraries
|
||||
• Goal: Define and own v1beta3 JSON Schemas and supporting defaulting/validation/conversion libraries within performance budgets.
|
||||
• Do:
|
||||
• Implement “API v1beta3 & schema work (deeper)” sections A–D (JSON Schema strategy; defaulting; validation; performance budget).
|
||||
• Add converters and fuzzers per “C) Converters robustness”.
|
||||
• Benchmarks per “D) Performance budget”.
|
||||
• Acceptance:
|
||||
• Schemas published under schemas.troubleshoot.sh/v1beta3/* with $id, $schema, $defs.
|
||||
• Validation/defaulting return structured errors; fuzz and perf budgets pass.
|
||||
|
||||
4) Preflight requirements disclosure command
|
||||
• Goal: Let customers preview requirements offline; render table/json/yaml/md; support templating values.
|
||||
• Do:
|
||||
• Implement “Preflight requirements disclosure (new command)” (`preflight requirements`), including flags and behaviors.
|
||||
• Implement templating from “Preflight CLI: Values and --set support (templating)”.
|
||||
• Acceptance:
|
||||
• Output validates against docs/preflight-requirements.schema.json and renders within width targets.
|
||||
• Unit and golden tests for table/json/md; fuzz tests for extractor stability.
|
||||
|
||||
5) Docs generator and portal gate/override
|
||||
• Goal: Generate preflight docs with rationale and support portal gate/override flow.
|
||||
• Do:
|
||||
• Implement “Preflight docs & portal flow (hardening)” sections A–D (merge engine, docs generator, portal client contract, E2E tests).
|
||||
• Ensure CLI prints requestId on error; implement backoff/idempotency per contract.
|
||||
• Acceptance:
|
||||
• E2E portal tests cover pass/fail/override/429/5xx with retries.
|
||||
• Docs generator emits MD/HTML with i18n hooks and template slots.
|
||||
|
||||
6) Simplified spec model: intents, presets, imports
|
||||
• Goal: Reduce authoring burden via intents for collect/analyze, redaction profiles with tokenize, and preset/import model.
|
||||
• Do:
|
||||
• Implement “Simplified spec model: intents, presets, imports”: intents.collect.auto; intents.analyze.requirements; redact.profile + tokenize; import/extends; selectors/filters; compatibility flags `--emit` and `--explain`.
|
||||
• Provide examples and downgrade warnings for v1beta2 emit.
|
||||
• Acceptance:
|
||||
• Deterministic expansion demonstrated; explain output shows generated low-level spec; downgrade warnings reported where applicable.
|
||||
|
||||
7) Public packages & ecosystem factoring
|
||||
• Goal: Establish stable package boundaries to support reuse and avoid logging in libs.
|
||||
• Do:
|
||||
• Create packages listed under “Public packages & ecosystem” (pkg/cli/contract, update, schema, specs/*, docs/render, portal/client).
|
||||
• Export minimal, stable APIs; return structured errors.
|
||||
• Acceptance:
|
||||
• api-diff green or change proposal attached.
|
||||
|
||||
8) CI/CD reinforcement
|
||||
• Goal: End-to-end pipelines for verification, install matrix, benchmarks, supply-chain, and releases.
|
||||
• Do:
|
||||
• Implement pipeline stages listed under “CI/CD reinforcement → Pipelines 1–5”.
|
||||
• Add static checks (revive/golangci-lint, api-diff rules) per roadmap.
|
||||
• Acceptance:
|
||||
• Pipelines green; supply chain artifacts (SBOM, cosign, SLSA) produced; release flow notarizes and publishes.
|
||||
|
||||
9) Testing strategy, determinism and performance harness, artifacts layout
|
||||
• Goal: Comprehensive unit/contract/fuzz/integration tests, deterministic outputs, and curated fixtures.
|
||||
• Do:
|
||||
• Implement “Testing strategy (Dev 1 scope)” (unit, contract/golden, fuzz/property, integration/matrix tests).
|
||||
• Implement “Determinism & performance” harness and budgets.
|
||||
• Organize artifacts per “Artifacts & layout” and add Make targets for test/fuzz/contracts/e2e/bench.
|
||||
• Acceptance:
|
||||
• Golden tests stable; determinism harness passes under SOURCE_DATE_EPOCH; benchmarks within budgets.
|
||||
|
||||
10) Packaging matrix expansion (optional later)
|
||||
• Goal: Expand beyond brew/curl to scoop and deb/rpm when desired.
|
||||
• Do:
|
||||
• Extend “D) Packaging matrix validation (CI)” to include scoop and deb/rpm installers and tests across OSes.
|
||||
• Acceptance:
|
||||
• Installers validated on ubuntu/macos/windows with smoke commands; macOS notarization verified.
|
||||
|
||||
Notes
|
||||
• Each phase references detailed specifications below. Implement phases in order; parallelize sub-items where safe.
|
||||
• If scope for an initial milestone is narrower (e.g., brew/curl only), mark the remaining items as deferred but keep tests/docs ready to expand.
|
||||
|
||||
### Cross-cutting engineering charters
|
||||
|
||||
1) Contract/stability policy (one pager, checked into repo)
|
||||
• SemVer & windows: major.minor.patch; flags/commands stable for ≥2 minors; deprecations carry --explain-deprecations.
|
||||
• Breaking-change gate: PR must include contracts/CHANGE_PROPOSAL.md + updated goldens + migration notes.
|
||||
• Determinism: Same inputs ⇒ byte-identical outputs (normalized map ordering, sorted slices, stable timestamps with SOURCE_DATE_EPOCH).
|
||||
|
||||
2) Observability & diagnostics
|
||||
• Structured logs (zerolog/zap): --log-format {text,json}, --log-level {info,debug,trace}.
|
||||
• Exit code taxonomy: 0 ok, 1 generic, 2 usage, 3 network, 4 schema, 5 incompatible-api, 6 update-failed, 7 permission, 8 partial-success.
|
||||
• OTel hooks (behind TROUBLESHOOT_OTEL_ENDPOINT): span “loadSpec”, “mergeSpec”, “runPreflight”, “uploadPortal”.
|
||||
|
||||
3) Reproducible, signed, attestable releases
|
||||
• SBOM (cyclonedx/spdx) emitted by GoReleaser.
|
||||
• cosign: sign archives + checksums.txt; produce SLSA provenance attestation.
|
||||
• SOURCE_DATE_EPOCH set in CI to pin archive mtimes.
|
||||
|
||||
CLI contracts & packaging (more depth)
|
||||
|
||||
A) Machine-readable CLI spec
|
||||
• Generate docs/cli-contracts.json from Cobra tree (name, synopsis, flags, defaults, env aliases, deprecation).
|
||||
• Validate at runtime when TROUBLESHOOT_DEBUG_CONTRACT=1 to catch drift in dev builds.
|
||||
• Use that JSON to:
|
||||
• Autogenerate shell completions for bash/zsh/fish/pwsh.
|
||||
• Render the --help text (single source of truth).
|
||||
|
||||
B) UX hardening
|
||||
• TTY detection: progress bars only on TTY; --no-progress to force off.
|
||||
• Color policy: --color {auto,always,never} + NO_COLOR env respected.
|
||||
• Output mode: --output {human,json,yaml} for all read commands. For json, include a top-level "schemaVersion": "cli.v1".
|
||||
|
||||
C) Update system (secure + rollback)
|
||||
• Channel support: --channel {stable,rc,nightly} (maps to tags: vX.Y.Z, vX.Y.Z-rc.N, nightly-YYYYMMDD).
|
||||
• Rollback: keep N=2 previous binaries under ~/.troubleshoot/bin/versions/…; preflight update --rollback.
|
||||
• Tamper defense: verify cosign sig for checksums.txt; verify SHA256 of selected asset; fail closed with error code 6.
|
||||
• Delta updates (optional later): if asset .patch exists and base version matches, apply bsdiff; fallback to full.
|
||||
|
||||
D) Packaging matrix validation (CI)
|
||||
• Matrix test on ubuntu-latest, macos-latest, windows-latest:
|
||||
• Install via brew, scoop, deb/rpm, curl|bash; then run preflight --version and a sample command.
|
||||
• Gatekeeper: spctl -a -v on macOS; print notarization ticket.
|
||||
|
||||
E) Config precedence & env aliases
|
||||
• Per-binary config paths (defaults):
|
||||
• macOS/Linux:
|
||||
• preflight: ~/.config/preflight/config.yaml
|
||||
• support-bundle: ~/.config/support-bundle/config.yaml
|
||||
• Windows:
|
||||
• preflight: %APPDATA%\Troubleshoot\Preflight\config.yaml
|
||||
• support-bundle: %APPDATA%\Troubleshoot\SupportBundle\config.yaml
|
||||
• Optional global fallback (lower precedence): ~/.config/troubleshoot/config.yaml
|
||||
• Precedence: flag > binary env > global env > binary config > global config > default
|
||||
• --config <path> overrides discovery; respects XDG_CONFIG_HOME (Unix) and APPDATA (Windows)
|
||||
• Env aliases:
|
||||
• Global: TROUBLESHOOT_PORTAL_URL, TROUBLESHOOT_API_TOKEN
|
||||
• Binary-scoped: PREFLIGHT_* and SUPPORT_BUNDLE_* (take precedence over TROUBLESHOOT_*)
|
||||
|
||||
F) Make targets
|
||||
|
||||
make contracts # regen CLI JSON + goldens
|
||||
make sbom # build SBOMs
|
||||
make release-dryrun # goreleaser --skip-publish
|
||||
make e2e-install # spins a container farm to test deb/rpm
|
||||
|
||||
|
||||
API v1beta3 & schema work (deeper)
|
||||
|
||||
A) JSON Schema strategy
|
||||
• Give every schema an $id and $schema; publish at schemas.troubleshoot.sh/v1beta3/*.json.
|
||||
• Use $defs for shared primitives (Quantity, Duration, CPUSet, Selector).
|
||||
• Add x-kubernetes-validations parity constraints where applicable (even if not applying as CRD).
|
||||
|
||||
B) Defaulting & validation library
|
||||
• pkg/validation/validate.go: returns []FieldError with JSONPointer paths and machine codes.
|
||||
• pkg/defaults/defaults.go: idempotent defaulting; fuzz tests prove no oscillation (fuzz: in -> default -> default == default).
|
||||
|
||||
C) Converters robustness
|
||||
• Fuzzers (go1.20+): generate random v1beta1/2 structs, convert→internal→v1beta3→internal and assert invariants (lossless roundtrips where representable).
|
||||
• Report downgrade loss: if v1beta3→v1beta2 drops info, print warning list to stderr and annotate output with x-downgrade-warnings.
|
||||
|
||||
D) Performance budget
|
||||
• Load+validate 1MB spec ≤ 150ms p95, 10MB ≤ 800ms p95 on GOARCH=amd64 GitHub runner.
|
||||
• Benchmarks in pkg/apis/bench_test.go enforce budgets.
|
||||
|
||||
E) Simplified spec model: intents, presets, imports
|
||||
• Problem: vendors handwrite verbose collector/analyzer lists. Goal: smaller, intent-driven specs that expand deterministically.
|
||||
• Tenets:
|
||||
• Additive, backwards-compatible; loader can expand intents into concrete v1beta2-equivalent structures.
|
||||
• Deterministic expansion (same inputs ⇒ same expansion) with --explain to show the generated low-level spec.
|
||||
• Shorthand over raw lists: “what” not “how”.
|
||||
• Top-level additions (v1beta3):
|
||||
• intents.collect.auto: namespace, profiles, includeKinds, excludeKinds, selectors, size caps.
|
||||
• intents.analyze.requirements: high-level checks (k8sVersion, nodes.cpu/memory, podsReady, storageClass, CRDsPresent…).
|
||||
• redact.profile + tokenize: standard|strict; optional token map emission.
|
||||
• import: versioned presets (preset://k8s/basic@v1) with local vendoring.
|
||||
• extends: URL or preset to inherit from, with override blocks.
|
||||
• Selectors & filters:
|
||||
• labelSelector, fieldSelector, name/glob filters; include/exclude precedence clarified in schema docs.
|
||||
• Compatibility:
|
||||
• --emit v1beta2 to produce a concrete legacy spec; downgrade warnings if some intent can’t fully map.
|
||||
• --explain prints the expanded collectors/analyzers to aid review and vendoring.
|
||||
• Example: Preflight with requirements + docs
|
||||
|
||||
```yaml
|
||||
apiVersion: troubleshoot.sh/v1beta3
|
||||
kind: Preflight
|
||||
metadata:
|
||||
name: example
|
||||
requirements:
|
||||
- name: Baseline
|
||||
docString: "Core Kubernetes and cluster requirements."
|
||||
checks:
|
||||
- clusterVersion:
|
||||
checkName: Kubernetes version
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "< 1.20.0"
|
||||
message: This application requires at least Kubernetes 1.20.0, and recommends 1.22.0.
|
||||
uri: https://kubernetes.io
|
||||
- warn:
|
||||
when: "< 1.22.0"
|
||||
message: Your cluster meets the minimum version of Kubernetes, but we recommend you update to 1.22.0 or later.
|
||||
uri: https://kubernetes.io
|
||||
- pass:
|
||||
when: ">= 1.22.0"
|
||||
message: Your cluster meets the recommended and required versions of Kubernetes.
|
||||
- customResourceDefinition:
|
||||
checkName: Ingress
|
||||
customResourceDefinitionName: ingressroutes.contour.heptio.com
|
||||
outcomes:
|
||||
- fail:
|
||||
message: Contour ingress not found!
|
||||
- pass:
|
||||
message: Contour ingress found!
|
||||
- containerRuntime:
|
||||
outcomes:
|
||||
- pass:
|
||||
when: "== containerd"
|
||||
message: containerd container runtime was found.
|
||||
- fail:
|
||||
message: Did not find containerd container runtime.
|
||||
- storageClass:
|
||||
checkName: Required storage classes
|
||||
storageClassName: "default"
|
||||
outcomes:
|
||||
- fail:
|
||||
message: Could not find a storage class called default.
|
||||
- pass:
|
||||
message: All good on storage classes
|
||||
- distribution:
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "== docker-desktop"
|
||||
message: The application does not support Docker Desktop Clusters
|
||||
- fail:
|
||||
when: "== microk8s"
|
||||
message: The application does not support Microk8s Clusters
|
||||
- fail:
|
||||
when: "== minikube"
|
||||
message: The application does not support Minikube Clusters
|
||||
- pass:
|
||||
when: "== eks"
|
||||
message: EKS is a supported distribution
|
||||
- pass:
|
||||
when: "== gke"
|
||||
message: GKE is a supported distribution
|
||||
- pass:
|
||||
when: "== aks"
|
||||
message: AKS is a supported distribution
|
||||
- pass:
|
||||
when: "== kurl"
|
||||
message: KURL is a supported distribution
|
||||
- pass:
|
||||
when: "== digitalocean"
|
||||
message: DigitalOcean is a supported distribution
|
||||
- pass:
|
||||
when: "== rke2"
|
||||
message: RKE2 is a supported distribution
|
||||
- pass:
|
||||
when: "== k3s"
|
||||
message: K3S is a supported distribution
|
||||
- pass:
|
||||
when: "== oke"
|
||||
message: OKE is a supported distribution
|
||||
- pass:
|
||||
when: "== kind"
|
||||
message: Kind is a supported distribution
|
||||
- warn:
|
||||
message: Unable to determine the distribution of Kubernetes
|
||||
- nodeResources:
|
||||
checkName: Must have at least 3 nodes in the cluster, with 5 recommended
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "count() < 3"
|
||||
message: This application requires at least 3 nodes.
|
||||
uri: https://kurl.sh/docs/install-with-kurl/adding-nodes
|
||||
- warn:
|
||||
when: "count() < 5"
|
||||
message: This application recommends at last 5 nodes.
|
||||
uri: https://kurl.sh/docs/install-with-kurl/adding-nodes
|
||||
- pass:
|
||||
message: This cluster has enough nodes.
|
||||
- nodeResources:
|
||||
checkName: Every node in the cluster must have at least 8 GB of memory, with 32 GB recommended
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "min(memoryCapacity) < 8Gi"
|
||||
message: All nodes must have at least 8 GB of memory.
|
||||
uri: https://kurl.sh/docs/install-with-kurl/system-requirements
|
||||
- warn:
|
||||
when: "min(memoryCapacity) < 32Gi"
|
||||
message: All nodes are recommended to have at least 32 GB of memory.
|
||||
uri: https://kurl.sh/docs/install-with-kurl/system-requirements
|
||||
- pass:
|
||||
message: All nodes have at least 32 GB of memory.
|
||||
- nodeResources:
|
||||
checkName: Total CPU Cores in the cluster is 4 or greater
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "sum(cpuCapacity) < 4"
|
||||
message: The cluster must contain at least 4 cores
|
||||
uri: https://kurl.sh/docs/install-with-kurl/system-requirements
|
||||
- pass:
|
||||
message: There are at least 4 cores in the cluster
|
||||
- nodeResources:
|
||||
checkName: Every node in the cluster must have at least 40 GB of ephemeral storage, with 100 GB recommended
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "min(ephemeralStorageCapacity) < 40Gi"
|
||||
message: All nodes must have at least 40 GB of ephemeral storage.
|
||||
uri: https://kurl.sh/docs/install-with-kurl/system-requirements
|
||||
- warn:
|
||||
when: "min(ephemeralStorageCapacity) < 100Gi"
|
||||
message: All nodes are recommended to have at least 100 GB of ephemeral storage.
|
||||
uri: https://kurl.sh/docs/install-with-kurl/system-requirements
|
||||
- pass:
|
||||
message: All nodes have at least 100 GB of ephemeral storage.
|
||||
|
||||
{{- if eq .Values.postgres.enabled true }}
|
||||
- name: Postgres
|
||||
docString: "Postgres needs a storage class and sufficient memory."
|
||||
checks:
|
||||
- storageClass:
|
||||
checkName: Postgres storage class
|
||||
name: "{{ .Values.postgres.storageClassName | default \"default\" }}"
|
||||
required: true
|
||||
- nodeResources:
|
||||
checkName: Postgres memory guidance
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "min(memoryCapacity) < 8Gi"
|
||||
message: All nodes must have at least 8 GB of memory for Postgres.
|
||||
- warn:
|
||||
when: "min(memoryCapacity) < 32Gi"
|
||||
message: Nodes are recommended to have at least 32 GB of memory for Postgres.
|
||||
- pass:
|
||||
message: Nodes have sufficient memory for Postgres.
|
||||
{{- end }}
|
||||
|
||||
{{- if eq .Values.redis.enabled true }}
|
||||
- name: Redis
|
||||
docString: "Redis needs a storage class and adequate ephemeral storage."
|
||||
checks:
|
||||
- storageClass:
|
||||
checkName: Redis storage class
|
||||
name: "{{ .Values.redis.storageClassName | default \"default\" }}"
|
||||
required: true
|
||||
- nodeResources:
|
||||
checkName: Redis ephemeral storage
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "min(ephemeralStorageCapacity) < 40Gi"
|
||||
message: All nodes must have at least 40 GB of ephemeral storage for Redis.
|
||||
- warn:
|
||||
when: "min(ephemeralStorageCapacity) < 100Gi"
|
||||
message: Nodes are recommended to have at least 100 GB of ephemeral storage for Redis.
|
||||
- pass:
|
||||
message: Nodes have sufficient ephemeral storage for Redis.
|
||||
{{- end }}
|
||||
```
|
||||
|
||||
• Presets library:
|
||||
• Versioned URIs (e.g., preset://k8s/basic@v1, preset://app/logs@v1) maintained in-repo and publishable.
|
||||
• "troubleshoot vendor --import" downloads presets to ./vendor/troubleshoot/ for offline builds.
|
||||
|
||||
Preflight docs & portal flow (hardening)
|
||||
|
||||
A) Merge engine details
|
||||
• Stable key = GroupKind/Name[/Namespace] (e.g., NodeResource/CPU, FilePermission//etc/hosts).
|
||||
• Conflict detection emits a list with reasons: “same key, differing fields: thresholds.min, description”.
|
||||
• Provenance captured on each merged node:
|
||||
• troubleshoot.sh/provenance: vendor|replicated|merged
|
||||
• troubleshoot.sh/merge-conflict: "thresholds.min, description"
|
||||
|
||||
B) Docs generator upgrades
|
||||
• Template slots: why, riskLevel {low,med,high}, owner, runbookURL, estimatedTime.
|
||||
• i18n hooks: template lookup by locale --locale es-ES falls back to en-US.
|
||||
• Output MD + self-contained HTML (inline CSS) when --html. --toc adds a nav sidebar.
|
||||
|
||||
C) Portal client contract
|
||||
• Auth: Bearer <token>; optional mTLS later.
|
||||
• Idempotency: Idempotency-Key header derived from spec SHA256.
|
||||
• Backoff: exponential jitter (100ms → 3s, 6 tries) on 429/5xx; code 3 on exhaustion.
|
||||
• Response model:
|
||||
|
||||
{
|
||||
"requestId": "r_abc123",
|
||||
"decision": "pass|override|fail",
|
||||
"reason": "text",
|
||||
"policyVersion": "2025-09-01"
|
||||
}
|
||||
|
||||
• CLI prints requestId on error for support.
|
||||
|
||||
D) E2E tests (httptest.Server)
|
||||
• Scenarios: pass, fail, override, 429 with retry-after, 5xx flake, invalid JSON.
|
||||
• Golden transcripts of HTTP exchanges under testdata/e2e/portal.
|
||||
|
||||
|
||||
Public packages & ecosystem
|
||||
|
||||
A) Package boundaries
|
||||
|
||||
pkg/
|
||||
cli/contract # cobra->json exporter (no cobra import cycles)
|
||||
update/ # channel, verify, rollback
|
||||
schema/ # embed.FS of JSON Schemas + helpers
|
||||
specs/loader # version sniffing, load any -> internal
|
||||
specs/convert # converters
|
||||
specs/validate # validation library
|
||||
docs/render # md/html generation
|
||||
portal/client # http client + types
|
||||
|
||||
• No logging in libs; return structured errors with codes; callers log.
|
||||
|
||||
B) SARIF export (nice-to-have)
|
||||
• --output sarif for preflight results so CI systems ingest findings.
|
||||
|
||||
C) Back-compat façade
|
||||
• For integrators, add tiny shim: pkg/legacy/v1beta2loader that calls new loader + converter; mark with Deprecated: GoDoc but stable for a window.
|
||||
|
||||
CI/CD reinforcement
|
||||
|
||||
Pipelines
|
||||
1. verify: lint, unit, fuzz (short), contracts, schemas → required.
|
||||
2. matrix-install: brew/scoop/deb/rpm/curl on 3 OSes.
|
||||
3. bench: enforce perf budgets.
|
||||
4. supply-chain: build SBOM, cosign sign/verify, slsa attestation.
|
||||
5. release (tagged): goreleaser publish, notarize, bump brew/scoop, attach SBOM, cosign attest.
|
||||
|
||||
Static checks
|
||||
• revive/golangci-lint with a rule to forbid time.Now() in pure functions; must use injected clock.
|
||||
• api-diff: compare exported pkg/** against last tag; fails on breaking changes without contracts/CHANGE_PROPOSAL.md.
|
||||
|
||||
1) Error codes (centralized)
|
||||
|
||||
package xerr
|
||||
type Code int
|
||||
const (
|
||||
OK Code = iota
|
||||
Usage
|
||||
Network
|
||||
Schema
|
||||
IncompatibleAPI
|
||||
UpdateFailed
|
||||
Permission
|
||||
Partial
|
||||
)
|
||||
type E struct { Code Code; Op, Msg string; Err error }
|
||||
func (e *E) Error() string { return e.Msg }
|
||||
func CodeOf(err error) Code { /* unwrap */ }
|
||||
|
||||
2) Output envelope (JSON mode)
|
||||
|
||||
{
|
||||
"schemaVersion": "cli.v1",
|
||||
"tool": "preflight",
|
||||
"version": "1.12.0",
|
||||
"timestamp": "2025-09-09T17:02:33Z",
|
||||
"result": { /* command-specific */ },
|
||||
"warnings": [],
|
||||
"errors": []
|
||||
}
|
||||
|
||||
3) Idempotency key
|
||||
|
||||
func idemKey(spec []byte) string {
|
||||
sum := sha256.Sum256(spec)
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
4) Deterministic marshaling
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetEscapeHTML(false)
|
||||
enc.SetIndent("", " ")
|
||||
sort.SliceStable(obj.Items, func(i,j int) bool { return obj.Items[i].Name < obj.Items[j].Name })
|
||||
|
||||
Measurable add-on success criteria
|
||||
• preflight --help --output json validates against docs/cli-contracts.schema.json.
|
||||
• make bench passes with stated p95 budgets.
|
||||
• cosign verify-blob succeeds for checksums.txt in CI and on dev machines (doc’d).
|
||||
• E2E portal tests cover all decision branches and 429/5xx paths with retries observed.
|
||||
• api-diff is green or has an attached change proposal.
|
||||
|
||||
Testing strategy (Dev 1 scope)
|
||||
|
||||
Unit tests
|
||||
• CLI arg parsing: Cobra ExecuteC with table-driven flag sets for both binaries.
|
||||
• Config precedence resolver: tmp dirs + OS-specific cases (XDG_CONFIG_HOME/APPDATA).
|
||||
• Validation/defaulting libraries: happy/edge cases; structured []FieldError assertions.
|
||||
• Portal client: httptest.Server scenarios (pass/fail/override/429/5xx) with retry/backoff checks.
|
||||
• Updater: mock release index; cosign verify using test keys; rollback success/failure paths.
|
||||
|
||||
Contract/golden tests
|
||||
• CLI contracts: generate docs/cli-contracts.json and compare to goldens; update via make contracts.
|
||||
• --help rendering snapshots (normalized width/colors) for core commands.
|
||||
• Schemas: validate example specs against v1beta3 JSON Schemas; store fixtures in testdata/schemas/.
|
||||
• Docs generator: preflight-docs.md/HTML goldens for sample merged specs with provenance.
|
||||
|
||||
Fuzz/property tests
|
||||
• Converters: v1beta1/2→internal→v1beta3→internal round-trip fuzz; invariants enforced.
|
||||
• Defaulting idempotence: default(default(x)) == default(x).
|
||||
|
||||
Integration/matrix tests
|
||||
• Installers: brew/scoop/deb/rpm/curl on ubuntu/macos/windows; run preflight/support-bundle --version and a smoke command.
|
||||
• macOS notarization: spctl -a -v on built binaries.
|
||||
• Updater E2E: start mock release server, switch channels, rollback, tamper-detection failure.
|
||||
|
||||
Determinism & performance
|
||||
• Deterministic outputs under SOURCE_DATE_EPOCH; byte-for-byte stable archives in a test harness.
|
||||
• Benchmarks: load+validate budgets (latency + RSS) enforced via go test -bench and thresholds.
|
||||
|
||||
Artifacts & layout
|
||||
• Fixtures under testdata/: schemas/, cli/, docs/, portal/, updater/ with README explaining regeneration.
|
||||
• Make targets: make test, make fuzz-short, make contracts, make e2e-install, make bench.
|
||||
|
||||
Preflight CLI: Values and --set support (templating)
|
||||
|
||||
• Goal: Let end customers pass Values at runtime to drive a single modular YAML with conditionals.
|
||||
• Scope: `preflight` gains `--values` (repeatable) and `--set key=value` (repeatable), rendered over the input YAML before loading specs.
|
||||
• Template engine: Go text/template + Sprig, with `.Values` bound. Standard delimiters `{{` `}}`.
|
||||
• Precedence:
|
||||
• `--set` overrides everything (last one wins when repeated)
|
||||
• Later `--values` files override earlier ones (left-to-right deep merge)
|
||||
• Defaults embedded in the YAML are lowest precedence
|
||||
• Merge:
|
||||
• Maps: deep-merge
|
||||
• Slices: replace (whole list)
|
||||
• Types:
|
||||
• `true|false` parsed as bool, numbers as float/int when unquoted, everything else as string
|
||||
• Use quotes to force string: `--set image.tag="1.2.3"`
|
||||
|
||||
Example usage
|
||||
|
||||
```bash
|
||||
# combine file values with inline overrides
|
||||
preflight ./some-preflight-checks.yaml \
|
||||
--values ./values.yaml \
|
||||
--values ./values-prod.yaml \
|
||||
--set postgres.enabled=true \
|
||||
--set redis.enabled=false
|
||||
```
|
||||
|
||||
Minimal Values schema (illustrative)
|
||||
|
||||
```yaml
|
||||
postgres:
|
||||
enabled: false
|
||||
storageClassName: default
|
||||
redis:
|
||||
enabled: true
|
||||
storageClassName: default
|
||||
```
|
||||
|
||||
Single-file modular YAML authoring pattern
|
||||
|
||||
```yaml
|
||||
apiVersion: troubleshoot.sh/v1beta3
|
||||
kind: Preflight
|
||||
metadata:
|
||||
name: example
|
||||
requirements:
|
||||
- name: Baseline
|
||||
docString: "Core Kubernetes requirements."
|
||||
checks:
|
||||
- k8sVersion: ">=1.22"
|
||||
- distribution:
|
||||
allow: [eks, gke, aks, kurl, digitalocean, rke2, k3s, oke, kind]
|
||||
deny: [docker-desktop, microk8s, minikube]
|
||||
- storageClass:
|
||||
name: "default"
|
||||
required: true
|
||||
|
||||
{{- if eq .Values.postgres.enabled true }}
|
||||
- name: Postgres
|
||||
docString: "Postgres needs a storage class and sufficient memory."
|
||||
checks:
|
||||
- storageClass:
|
||||
name: "{{ .Values.postgres.storageClassName | default \"default\" }}"
|
||||
required: true
|
||||
- nodes:
|
||||
memoryPerNode: ">=8Gi"
|
||||
recommendMemoryPerNode: ">=32Gi"
|
||||
{{- end }}
|
||||
|
||||
{{- if eq .Values.redis.enabled true }}
|
||||
- name: Redis
|
||||
docString: "Redis needs a storage class and adequate ephemeral storage."
|
||||
checks:
|
||||
- storageClass:
|
||||
name: "{{ .Values.redis.storageClassName | default \"default\" }}"
|
||||
required: true
|
||||
- nodes:
|
||||
ephemeralPerNode: ">=40Gi"
|
||||
recommendEphemeralPerNode: ">=100Gi"
|
||||
{{- end }}
|
||||
```
|
||||
|
||||
Notes
|
||||
• Keep everything in one YAML; conditionals gate entire requirement blocks.
|
||||
• Authors can still drop down to raw analyzers; the renderer runs before spec parsing, so both styles work.
|
||||
• Add `--dry-run` to print the rendered spec without executing checks.
|
||||
106
scheduled-job-daemon-explained.md
Normal file
106
scheduled-job-daemon-explained.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# Scheduled Jobs + Daemon: How They Work Together
|
||||
|
||||
## The Complete Picture
|
||||
|
||||
```
|
||||
You create scheduled jobs → Daemon watches jobs → Jobs run automatically
|
||||
|
||||
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||
│ Scheduled Job │ │ Daemon Process │ │ Job Execution │
|
||||
│ │ │ │ │ │
|
||||
│ Name: daily │───▶│ ⏰ Checks time │───▶│ ▶ Collect bundle│
|
||||
│ Schedule: 2 AM │ │ 📋 Reads jobs │ │ ▶ Upload to S3 │
|
||||
│ Task: collect │ │ 🔄 Runs loop │ │ ▶ Send alerts │
|
||||
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
## Step-by-Step Example
|
||||
|
||||
### 1. You Create a Scheduled Job (One Time Setup)
|
||||
```bash
|
||||
support-bundle schedule create daily-health-check \
|
||||
--cron "0 2 * * *" \
|
||||
--namespace production \
|
||||
--auto \
|
||||
--upload enabled
|
||||
```
|
||||
|
||||
**What this creates:**
|
||||
- A job definition stored on disk
|
||||
- Schedule: "Run daily at 2:00 AM"
|
||||
- Task: "Collect support bundle from production namespace with auto-discovery and auto-upload to vendor portal"
|
||||
|
||||
### 2. You Start the Daemon (One Time Setup)
|
||||
```bash
|
||||
support-bundle schedule daemon start
|
||||
```
|
||||
|
||||
**What the daemon does:**
|
||||
```go
|
||||
// Simplified daemon logic
|
||||
for {
|
||||
currentTime := time.Now()
|
||||
|
||||
// Check all scheduled jobs
|
||||
for _, job := range scheduledJobs {
|
||||
if job.NextRunTime <= currentTime && job.Enabled {
|
||||
go runSupportBundleCollection(job) // Run in background
|
||||
job.NextRunTime = calculateNextRun(job.Schedule)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(60 * time.Second) // Wait 1 minute, then check again
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Automatic Execution (Happens Forever)
|
||||
```
|
||||
Day 1, 2:00 AM → Daemon sees it's time → Runs: support-bundle --namespace production
|
||||
Day 2, 2:00 AM → Daemon sees it's time → Runs: support-bundle --namespace production
|
||||
Day 3, 2:00 AM → Daemon sees it's time → Runs: support-bundle --namespace production
|
||||
... continues forever ...
|
||||
```
|
||||
|
||||
## Key Benefits
|
||||
|
||||
### Without Scheduling (Manual)
|
||||
```bash
|
||||
# You have to remember to run this every day
|
||||
support-bundle --namespace production
|
||||
# Upload manually
|
||||
# Check results manually
|
||||
# Easy to forget!
|
||||
```
|
||||
|
||||
### With Scheduling (Automatic)
|
||||
```bash
|
||||
# Set it up once
|
||||
support-bundle schedule create daily-check --cron "0 2 * * *" --namespace production --auto --upload enabled
|
||||
|
||||
# Start daemon once
|
||||
support-bundle schedule daemon start
|
||||
|
||||
# Now it happens automatically forever:
|
||||
# ✓ Collects support bundle daily at 2 AM with auto-discovery
|
||||
# ✓ Auto-uploads to vendor portal automatically
|
||||
# ✓ Never forgets
|
||||
# ✓ You can sleep peacefully!
|
||||
```
|
||||
|
||||
## Real-World Comparison
|
||||
|
||||
### Scheduled Job = Appointment in Calendar
|
||||
- **Job Definition**: "Doctor appointment every 6 months"
|
||||
- **Schedule**: "Next Tuesday at 3 PM"
|
||||
- **Task**: "Go to doctor for checkup"
|
||||
|
||||
### Daemon = Personal Assistant
|
||||
- **Always watching**: Checks your calendar continuously
|
||||
- **Reminds you**: "It's time for your doctor appointment!"
|
||||
- **Manages conflicts**: "You have 3 appointments at once, let me reschedule"
|
||||
- **Never sleeps**: Works 24/7 even when you're busy
|
||||
|
||||
### In Troubleshoot Terms
|
||||
- **Scheduled Job**: "Collect diagnostics every 6 hours from namespace 'webapp'"
|
||||
- **Daemon**: Background service that watches the clock and runs collections automatically
|
||||
- **Result**: Continuous monitoring without manual intervention
|
||||
Reference in New Issue
Block a user