drop kubectl-pg plugin (#3107)

Co-authored-by: Ida Novindasari <idanovinda@gmail.com>
This commit is contained in:
Felix Kunde 2026-06-12 10:42:37 +02:00 committed by GitHub
parent a71e6bdf7f
commit ebf48667f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 0 additions and 2123 deletions

1
.gitignore vendored
View File

@ -26,7 +26,6 @@ _testmain.go
*.test
*.prof
/vendor/
/kubectl-pg/vendor/
/build/
/docker/build/
/github.com/

View File

@ -1,137 +0,0 @@
# Kubectl Plugin for Zalando's Postgres Operator
This plugin is a prototype developed as a part of **Google Summer of Code 2019** under the [Postgres Operator](https://summerofcode.withgoogle.com/archive/2019/organizations/6187982082539520/) organization.
## Installation of kubectl pg plugin
This project uses Go Modules for dependency management to build locally.
Install go and enable go modules with ```export GO111MODULE=on```.
From Go >=1.13 Go modules will be enabled by default.
```bash
# Assumes you have a working KUBECONFIG
$ GO111MODULE="on"
$ GOPATH/src/github.com/zalando/postgres-operator/kubectl-pg go mod vendor
# This generate a vendor directory with all dependencies needed by the plugin.
$ $GOPATH/src/github.com/zalando/postgres-operator/kubectl-pg go install
# This will place the kubectl-pg binary in your $GOPATH/bin
```
## Before using the kubectl pg plugin make sure to set KUBECONFIG env variable
Ideally KUBECONFIG is found in $HOME/.kube/config else specify the KUBECONFIG path here.
```export KUBECONFIG=$HOME/.kube/config```
## List all commands available in kubectl pg
```kubectl pg --help``` (or) ```kubectl pg```
## Check if Postgres Operator is installed and running
```kubectl pg check```
## Create a new cluster using manifest file
```kubectl pg create -f acid-minimal-cluster.yaml```
## List postgres resources
```kubectl pg list```
List clusters across namespaces
```kubectl pg list all```
## Update existing cluster using manifest file
```kubectl pg update -f acid-minimal-cluster.yaml```
## Delete existing cluster
Using the manifest file:
```kubectl pg delete -f acid-minimal-cluster.yaml```
Or by specifying the cluster name:
```kubectl pg delete acid-minimal-cluster```
Use `--namespace` or `-n` flag if your cluster is in a different namespace to where your current context is pointing to:
```kubectl pg delete acid-minimal-cluster -n namespace01```
## Adding manifest roles to an existing cluster
```kubectl pg add-user USER01 -p CREATEDB,LOGIN -c acid-minimal-cluster```
Privileges can only be [SUPERUSER, REPLICATION, INHERIT, LOGIN, NOLOGIN, CREATEROLE, CREATEDB, BYPASSRLS]
Note: By default, a LOGIN user is created (unless NOLOGIN is specified).
## Adding databases to an existing cluster
You have to specify an owner of the new database and this role must already exist in the cluster:
```kubectl pg add-db DB01 -o OWNER01 -c acid-minimal-cluster```
## Extend the volume of an existing pg cluster
```kubectl pg ext-volume 2Gi -c acid-minimal-cluster```
## Print the version of Postgres Operator and kubectl pg plugin
```kubectl pg version```
## Connect to the shell of a postgres pod
Connect to the master pod:
```kubectl pg connect -c CLUSTER -m```
Connect to a random replica pod:
```kubectl pg connect -c CLUSTER```
Connect to a certain replica pod:
```kubectl pg connect -c CLUSTER -r 0```
## Connect to a database via psql
Adding the `-p` flag allows you to directly connect to a given database with the psql client.
With `-u` you specify the user. If left out the name of the current OS user is taken.
`-d` lets you specify the database. If no database is specified, it will be the same as the user name.
Connect to `app_db` database on the master with role `app_user`:
```kubectl pg connect -c CLUSTER -m -p -u app_user -d app_db```
Connect to the `postgres` database on a random replica with role `postgres`:
```kubectl pg connect -c CLUSTER -p -u postgres```
Connect to a certain replica assuming name of OS user, database role and name are all the same:
```kubectl pg connect -c CLUSTER -r 0 -p```
## Access Postgres Operator logs
```kubectl pg logs -o```
## Access Patroni logs of different database pods
Fetch logs of master:
```kubectl pg logs -c CLUSTER -m```
Fetch logs of a random replica pod:
```kubectl pg logs -c CLUSTER```
Fetch logs of specified replica
```kubectl pg logs -c CLUSTER -r 2```
## Development
When making changes to the plugin make sure to change the (major/patch) version of plugin in `build.sh` script and run `./build.sh`.
## Google Summer of Code 2019
### GSoC Proposal
[kubectl pg proposal](https://docs.google.com/document/d/1-WMy9HkfZ1XnnMbzplMe9rCzKrRMGaMz4owLVXXPb7w/edit)
### Weekly Reports
https://github.com/VineethReddy02/GSoC-Kubectl-Plugin-for-Postgres-Operator-tracker
### Final Project Report
https://gist.github.com/VineethReddy02/159283bd368a710379eaf0f6bd60a40a

View File

@ -1,4 +0,0 @@
VERSION=1.0
sed -i "s/KubectlPgVersion string = \"[^\"]*\"/KubectlPgVersion string = \"${VERSION}\"/" cmd/version.go
go install

View File

@ -1,114 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"context"
"encoding/json"
"fmt"
"log"
"github.com/spf13/cobra"
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
// addDbCmd represents the addDb command
var addDbCmd = &cobra.Command{
Use: "add-db",
Short: "Adds a DB and its owner to a Postgres cluster. The owner role is created if it does not exist",
Long: `Adds a new DB to the Postgres cluster. Owner needs to be specified by the -o flag, cluster with -c flag.`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) > 0 {
dbName := args[0]
dbOwner, _ := cmd.Flags().GetString("owner")
clusterName, _ := cmd.Flags().GetString("cluster")
addDb(dbName, dbOwner, clusterName)
} else {
fmt.Println("database name can't be empty. Use kubectl pg add-db [-h | --help] for more info")
}
},
Example: `
kubectl pg add-db db01 -o owner01 -c cluster01
`,
}
// add db and it's owner to the cluster
func addDb(dbName string, dbOwner string, clusterName string) {
config := getConfig()
postgresConfig, err := PostgresqlLister.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
namespace := getCurrentNamespace()
postgresql, err := postgresConfig.Postgresqls(namespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
if err != nil {
log.Fatal(err)
}
var dbOwnerExists bool
dbUsers := postgresql.Spec.Users
for key := range dbUsers {
if key == dbOwner {
dbOwnerExists = true
}
}
var patch []byte
// validating reserved DB names
if dbOwnerExists && dbName != "postgres" && dbName != "template0" && dbName != "template1" {
patch = dbPatch(dbName, dbOwner)
} else if !dbOwnerExists {
log.Fatal("The provided db-owner doesn't exist")
} else {
log.Fatal("The provided db-name is reserved by postgres")
}
updatedPostgres, err := postgresConfig.Postgresqls(namespace).Patch(context.TODO(), postgresql.Name, types.MergePatchType, patch, metav1.PatchOptions{})
if err != nil {
log.Fatal(err)
}
if updatedPostgres.ResourceVersion != postgresql.ResourceVersion {
fmt.Printf("Created new database %s with owner %s in PostgreSQL cluster %s.\n", dbName, dbOwner, updatedPostgres.Name)
} else {
fmt.Printf("postgresql %s is unchanged.\n", updatedPostgres.Name)
}
}
func dbPatch(dbname string, owner string) []byte {
ins := map[string]map[string]map[string]string{"spec": {"databases": {dbname: owner}}}
patchInstances, err := json.Marshal(ins)
if err != nil {
log.Fatal(err, "unable to parse patch for add-db")
}
return patchInstances
}
func init() {
addDbCmd.Flags().StringP("owner", "o", "", "provide owner of the database.")
addDbCmd.Flags().StringP("cluster", "c", "", "provide a postgres cluster name.")
rootCmd.AddCommand(addDbCmd)
}

View File

@ -1,144 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"context"
"encoding/json"
"fmt"
"log"
"strings"
"github.com/spf13/cobra"
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
var allowedPrivileges = []string{"SUPERUSER", "REPLICATION", "INHERIT", "LOGIN", "NOLOGIN", "CREATEROLE", "CREATEDB", "BYPASSRLS"}
// addUserCmd represents the addUser command
var addUserCmd = &cobra.Command{
Use: "add-user",
Short: "Adds a user to the postgres cluster with given privileges",
Long: `Adds a user to the postgres cluster. You can add privileges as well with -p flag.`,
Run: func(cmd *cobra.Command, args []string) {
clusterName, _ := cmd.Flags().GetString("cluster")
privileges, _ := cmd.Flags().GetString("privileges")
if len(args) > 0 {
user := args[0]
var permissions []string
var perms []string
if privileges != "" {
parsedRoles := strings.Replace(privileges, ",", " ", -1)
parsedRoles = strings.ToUpper(parsedRoles)
permissions = strings.Fields(parsedRoles)
var invalidPerms []string
for _, userPrivilege := range permissions {
validPerm := false
for _, privilege := range allowedPrivileges {
if privilege == userPrivilege {
perms = append(perms, userPrivilege)
validPerm = true
}
}
if !validPerm {
invalidPerms = append(invalidPerms, userPrivilege)
}
}
if len(invalidPerms) > 0 {
fmt.Printf("Invalid privileges %s\n", invalidPerms)
return
}
}
addUser(user, clusterName, perms)
}
},
Example: `
kubectl pg add-user user01 -p login,createdb -c cluster01
`,
}
// add user to the cluster with provided permissions
func addUser(user string, clusterName string, permissions []string) {
config := getConfig()
postgresConfig, err := PostgresqlLister.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
namespace := getCurrentNamespace()
postgresql, err := postgresConfig.Postgresqls(namespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
if err != nil {
log.Fatal(err)
}
setUsers := make(map[string]bool)
for _, k := range permissions {
setUsers[k] = true
}
if existingRoles, key := postgresql.Spec.Users[user]; key {
for _, k := range existingRoles {
setUsers[k] = true
}
}
Privileges := []string{}
for keys, values := range setUsers {
if values {
Privileges = append(Privileges, keys)
}
}
patch := applyUserPatch(user, Privileges)
updatedPostgresql, err := postgresConfig.Postgresqls(namespace).Patch(context.TODO(), postgresql.Name, types.MergePatchType, patch, metav1.PatchOptions{})
if err != nil {
log.Fatal(err)
}
if updatedPostgresql.ResourceVersion != postgresql.ResourceVersion {
fmt.Printf("postgresql %s is updated with new user %s and with privileges %s.\n", updatedPostgresql.Name, user, permissions)
} else {
fmt.Printf("postgresql %s is unchanged.\n", updatedPostgresql.Name)
}
}
func applyUserPatch(user string, value []string) []byte {
ins := map[string]map[string]map[string][]string{"spec": {"users": {user: value}}}
patchInstances, err := json.Marshal(ins)
if err != nil {
log.Fatal(err, "unable to parse number of instances json")
}
return patchInstances
}
func init() {
addUserCmd.Flags().StringP("cluster", "c", "", "add user to the provided cluster.")
addUserCmd.Flags().StringP("privileges", "p", "", "add privileges separated by commas without spaces")
rootCmd.AddCommand(addUserCmd)
}

View File

@ -1,74 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"context"
"fmt"
"log"
"github.com/spf13/cobra"
postgresConstants "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// checkCmd represent kubectl pg check.
var checkCmd = &cobra.Command{
Use: "check",
Short: "Checks the Postgres operator is installed in the k8s cluster",
Long: `Checks that the Postgres CRD is registered in a k8s cluster.
This means that the operator pod was able to start normally.`,
Run: func(cmd *cobra.Command, args []string) {
check()
},
Example: `
kubectl pg check
`,
}
// check validates postgresql CRD registered or not.
func check() *v1.CustomResourceDefinition {
config := getConfig()
apiExtClient, err := apiextv1.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
crdInfo, err := apiExtClient.CustomResourceDefinitions().Get(context.TODO(), postgresConstants.PostgresCRDResouceName, metav1.GetOptions{})
if err != nil {
log.Fatal(err)
}
if crdInfo.Name == postgresConstants.PostgresCRDResouceName {
fmt.Printf("Postgres Operator is installed in the k8s cluster.\n")
} else {
fmt.Printf("Postgres Operator is not installed in the k8s cluster.\n")
}
return crdInfo
}
func init() {
rootCmd.AddCommand(checkCmd)
}

View File

@ -1,144 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"context"
"log"
"os"
user "os/user"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/remotecommand"
)
// connectCmd represents the kubectl pg connect command
var connectCmd = &cobra.Command{
Use: "connect",
Short: "Connects to the shell prompt, psql prompt of postgres cluster",
Long: `Connects to the shell prompt, psql prompt of postgres cluster and also to specified replica or master.`,
Run: func(cmd *cobra.Command, args []string) {
clusterName, _ := cmd.Flags().GetString("cluster")
master, _ := cmd.Flags().GetBool("master")
replica, _ := cmd.Flags().GetString("replica")
psql, _ := cmd.Flags().GetBool("psql")
userName, _ := cmd.Flags().GetString("user")
dbName, _ := cmd.Flags().GetString("database")
if psql {
if userName == "" {
userInfo, err := user.Current()
if err != nil {
log.Fatal(err)
}
userName = userInfo.Username
}
}
if dbName == "" {
dbName = userName
}
connect(clusterName, master, replica, psql, userName, dbName)
},
Example: `
#connects to the master of postgres cluster
kubectl pg connect -c cluster -m
#connects to the random replica of postgres cluster
kubectl pg connect -c cluster
#connects to the provided replica number of postgres cluster
kubectl pg connect -c cluster -r 2
#connects to psql prompt of master for provided postgres cluster with current shell user
kubectl pg connect -c cluster -p -m
#connects to psql prompt of random replica for provided postgres cluster with provided user and db
kubectl pg connect -c cluster -p -u user01 -d db01
`,
}
func connect(clusterName string, master bool, replica string, psql bool, user string, dbName string) {
config := getConfig()
client, err := kubernetes.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
podName := getPodName(clusterName, master, replica)
var execRequest *rest.Request
if psql {
execRequest = client.CoreV1().RESTClient().Post().Resource("pods").
Name(podName).
Namespace(getCurrentNamespace()).
SubResource("exec").
Param("container", "postgres").
Param("command", "psql").
Param("command", dbName).
Param("command", user).
Param("stdin", "true").
Param("stdout", "true").
Param("stderr", "true").
Param("tty", "true")
} else {
execRequest = client.CoreV1().RESTClient().Post().Resource("pods").
Name(podName).
Namespace(getCurrentNamespace()).
SubResource("exec").
Param("container", "postgres").
Param("command", "su").
Param("command", "postgres").
Param("stdin", "true").
Param("stdout", "true").
Param("stderr", "true").
Param("tty", "true")
}
exec, err := remotecommand.NewSPDYExecutor(config, "POST", execRequest.URL())
if err != nil {
log.Fatal(err)
}
err = exec.StreamWithContext(context.TODO(), remotecommand.StreamOptions{
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
Tty: true,
})
if err != nil {
log.Fatal(err)
}
}
func init() {
connectCmd.Flags().StringP("cluster", "c", "", "provide the cluster name.")
connectCmd.Flags().BoolP("master", "m", false, "connect to master.")
connectCmd.Flags().StringP("replica", "r", "", "connect to replica. Specify replica number.")
connectCmd.Flags().BoolP("psql", "p", false, "connect to psql prompt.")
connectCmd.Flags().StringP("user", "u", "", "provide user.")
connectCmd.Flags().StringP("database", "d", "", "provide database name.")
rootCmd.AddCommand(connectCmd)
}

View File

@ -1,82 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"context"
"fmt"
"log"
"os"
"github.com/spf13/cobra"
v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// createCmd kubectl pg create.
var createCmd = &cobra.Command{
Use: "create",
Short: "Creates postgres object using manifest file",
Long: `Creates postgres custom resource objects from a manifest file.`,
Run: func(cmd *cobra.Command, args []string) {
fileName, _ := cmd.Flags().GetString("file")
create(fileName)
},
Example: `
kubectl pg create -f cluster-manifest.yaml
`,
}
// Create postgresql resources.
func create(fileName string) {
config := getConfig()
postgresConfig, err := PostgresqlLister.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
ymlFile, err := os.ReadFile(fileName)
if err != nil {
log.Fatal(err)
}
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode([]byte(ymlFile), nil, &v1.Postgresql{})
if err != nil {
log.Fatal(err)
}
postgresSql := obj.(*v1.Postgresql)
_, err = postgresConfig.Postgresqls(postgresSql.Namespace).Create(context.TODO(), postgresSql, metav1.CreateOptions{})
if err != nil {
log.Fatal(err)
}
fmt.Printf("postgresql %s created.\n", postgresSql.Name)
}
func init() {
createCmd.Flags().StringP("file", "f", "", "manifest file with the cluster definition.")
rootCmd.AddCommand(createCmd)
}

View File

@ -1,134 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"context"
"fmt"
"log"
"os"
"github.com/spf13/cobra"
v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// deleteCmd represents kubectl pg delete.
var deleteCmd = &cobra.Command{
Use: "delete",
Short: "Deletes postgresql object by cluster-name/manifest file",
Long: `Deletes the postgres objects identified by a manifest file or cluster-name.
Deleting the manifest is sufficient to delete the cluster.`,
Run: func(cmd *cobra.Command, args []string) {
namespace, _ := cmd.Flags().GetString("namespace")
file, _ := cmd.Flags().GetString("file")
if file != "" {
deleteByFile(file)
} else if namespace != "" {
if len(args) != 0 {
clusterName := args[0]
deleteByName(clusterName, namespace)
} else {
fmt.Println("cluster name can't be empty")
}
} else {
fmt.Println("use the flag either -n or -f to delete a resource.")
}
},
Example: `
#Deleting the postgres cluster using manifest file
kubectl pg delete -f cluster-manifest.yaml
#Deleting the postgres cluster using cluster name in current namespace.
kubectl pg delete cluster01
#Deleting the postgres cluster using cluster name in provided namespace
kubectl pg delete cluster01 -n namespace01
`,
}
func deleteByFile(file string) {
config := getConfig()
postgresConfig, err := PostgresqlLister.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
ymlFile, err := os.ReadFile(file)
if err != nil {
log.Fatal(err)
}
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode([]byte(ymlFile), nil, &v1.Postgresql{})
if err != nil {
log.Fatal(err)
}
postgresSql := obj.(*v1.Postgresql)
_, err = postgresConfig.Postgresqls(postgresSql.Namespace).Get(context.TODO(), postgresSql.Name, metav1.GetOptions{})
if err != nil {
fmt.Printf("Postgresql %s not found with the provided namespace %s : %s \n", postgresSql.Name, postgresSql.Namespace, err)
return
}
fmt.Printf("Are you sure you want to remove this PostgreSQL cluster? If so, please type (%s/%s) and hit Enter\n", postgresSql.Namespace, postgresSql.Name)
confirmAction(postgresSql.Name, postgresSql.Namespace)
err = postgresConfig.Postgresqls(postgresSql.Namespace).Delete(context.TODO(), postgresSql.Name, metav1.DeleteOptions{})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Postgresql %s deleted from %s.\n", postgresSql.Name, postgresSql.Namespace)
}
func deleteByName(clusterName string, namespace string) {
config := getConfig()
postgresConfig, err := PostgresqlLister.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
_, err = postgresConfig.Postgresqls(namespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
if err != nil {
fmt.Printf("Postgresql %s not found with the provided namespace %s : %s \n", clusterName, namespace, err)
return
}
fmt.Printf("Are you sure you want to remove this PostgreSQL cluster? If so, please type (%s/%s) and hit Enter\n", namespace, clusterName)
confirmAction(clusterName, namespace)
err = postgresConfig.Postgresqls(namespace).Delete(context.TODO(), clusterName, metav1.DeleteOptions{})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Postgresql %s deleted from %s.\n", clusterName, namespace)
}
func init() {
namespace := getCurrentNamespace()
deleteCmd.Flags().StringP("namespace", "n", namespace, "namespace of the cluster to be deleted.")
deleteCmd.Flags().StringP("file", "f", "", "manifest file with the cluster definition.")
rootCmd.AddCommand(deleteCmd)
}

View File

@ -1,119 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"context"
"encoding/json"
"fmt"
"log"
"strconv"
"github.com/spf13/cobra"
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
// extVolumeCmd represents the extVolume command
var extVolumeCmd = &cobra.Command{
Use: "ext-volume",
Short: "Increases the volume size of a given Postgres cluster",
Long: `Extends the volume of the postgres cluster. But volume cannot be shrinked.`,
Run: func(cmd *cobra.Command, args []string) {
clusterName, _ := cmd.Flags().GetString("cluster")
if len(args) > 0 {
volume := args[0]
extVolume(volume, clusterName)
} else {
fmt.Println("please enter the cluster name with -c flag & volume in desired units")
}
},
Example: `
#Extending the volume size of provided cluster
kubectl pg ext-volume 2Gi -c cluster01
`,
}
// extend volume with provided size & cluster name
func extVolume(increasedVolumeSize string, clusterName string) {
config := getConfig()
postgresConfig, err := PostgresqlLister.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
namespace := getCurrentNamespace()
postgresql, err := postgresConfig.Postgresqls(namespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
if err != nil {
log.Fatal(err)
}
oldSize, err := resource.ParseQuantity(postgresql.Spec.Volume.Size)
if err != nil {
log.Fatal(err)
}
newSize, err := resource.ParseQuantity(increasedVolumeSize)
if err != nil {
log.Fatal(err)
}
_, err = strconv.Atoi(newSize.String())
if err == nil {
fmt.Println("provide the valid volume size with respective units i.e Ki, Mi, Gi")
return
}
if newSize.Value() > oldSize.Value() {
patchInstances := volumePatch(newSize)
response, err := postgresConfig.Postgresqls(namespace).Patch(context.TODO(), postgresql.Name, types.MergePatchType, patchInstances, metav1.PatchOptions{})
if err != nil {
log.Fatal(err)
}
if postgresql.ResourceVersion != response.ResourceVersion {
fmt.Printf("%s volume is extended to %s.\n", response.Name, increasedVolumeSize)
} else {
fmt.Printf("%s volume %s is unchanged.\n", response.Name, postgresql.Spec.Volume.Size)
}
} else if newSize.Value() == oldSize.Value() {
fmt.Println("volume already has the desired size.")
} else {
fmt.Printf("volume %s size cannot be shrinked.\n", postgresql.Spec.Volume.Size)
}
}
func volumePatch(volume resource.Quantity) []byte {
patchData := map[string]map[string]map[string]resource.Quantity{"spec": {"volume": {"size": volume}}}
patch, err := json.Marshal(patchData)
if err != nil {
log.Fatal(err, "unable to parse patch to extend volume")
}
return patch
}
func init() {
extVolumeCmd.Flags().StringP("cluster", "c", "", "provide cluster name.")
rootCmd.AddCommand(extVolumeCmd)
}

View File

@ -1,125 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"context"
"fmt"
"log"
"strconv"
"time"
"github.com/spf13/cobra"
v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
TrimCreateTimestamp = 6000000000
)
// listCmd represents kubectl pg list.
var listCmd = &cobra.Command{
Use: "list",
Short: "Lists all the resources of kind postgresql",
Long: `Lists all the info specific to postgresql objects.`,
Run: func(cmd *cobra.Command, args []string) {
allNamespaces, _ := cmd.Flags().GetBool("all-namespaces")
namespace, _ := cmd.Flags().GetString("namespace")
if allNamespaces {
list(allNamespaces, "")
} else {
list(allNamespaces, namespace)
}
},
Example: `
#Lists postgres cluster in current namespace
kubectl pg list
#Lists postgres clusters in all namespaces
kubectl pg list -A
`,
}
// list command to list postgres.
func list(allNamespaces bool, namespace string) {
config := getConfig()
postgresConfig, err := PostgresqlLister.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
listPostgres, err := postgresConfig.Postgresqls(namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
log.Fatal(err)
}
if len(listPostgres.Items) == 0 {
if namespace != "" {
fmt.Printf("No Postgresql clusters found in namespace: %v\n", namespace)
} else {
fmt.Println("No Postgresql clusters found in all namespaces")
}
return
}
if allNamespaces {
listAll(listPostgres)
} else {
listWithNamespace(listPostgres)
}
}
func listAll(listPostgres *v1.PostgresqlList) {
template := "%-32s%-16s%-12s%-12s%-12s%-12s%-12s\n"
fmt.Printf(template, "NAME", "STATUS", "INSTANCES", "VERSION", "AGE", "VOLUME", "NAMESPACE")
for _, pgObjs := range listPostgres.Items {
fmt.Printf(template, pgObjs.Name,
pgObjs.Status.PostgresClusterStatus,
strconv.Itoa(int(pgObjs.Spec.NumberOfInstances)),
pgObjs.Spec.PostgresqlParam.PgVersion,
time.Since(pgObjs.CreationTimestamp.Time).Truncate(TrimCreateTimestamp),
pgObjs.Spec.Size, pgObjs.Namespace)
}
}
func listWithNamespace(listPostgres *v1.PostgresqlList) {
template := "%-32s%-16s%-12s%-12s%-12s%-12s\n"
fmt.Printf(template, "NAME", "STATUS", "INSTANCES", "VERSION", "AGE", "VOLUME")
for _, pgObjs := range listPostgres.Items {
fmt.Printf(template, pgObjs.Name,
pgObjs.Status.PostgresClusterStatus,
strconv.Itoa(int(pgObjs.Spec.NumberOfInstances)),
pgObjs.Spec.PostgresqlParam.PgVersion,
time.Since(pgObjs.CreationTimestamp.Time).Truncate(TrimCreateTimestamp),
pgObjs.Spec.Size)
}
}
func init() {
listCmd.Flags().BoolP("all-namespaces", "A", false, "list pg resources across all namespaces.")
listCmd.Flags().StringP("namespace", "n", getCurrentNamespace(), "provide the namespace")
rootCmd.AddCommand(listCmd)
}

View File

@ -1,143 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"context"
"io"
"log"
"os"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
// logsCmd represents the logs command
var logsCmd = &cobra.Command{
Use: "logs",
Short: "This will fetch the logs of the specified postgres cluster & postgres operator",
Long: `Fetches the logs of the postgres cluster (i.e master( with -m flag) & replica with (-r 1 pod number) and without -m or -r connects to random replica`,
Run: func(cmd *cobra.Command, args []string) {
opLogs, _ := cmd.Flags().GetBool("operator")
clusterName, _ := cmd.Flags().GetString("cluster")
master, _ := cmd.Flags().GetBool("master")
replica, _ := cmd.Flags().GetString("replica")
if opLogs {
operatorLogs()
} else {
clusterLogs(clusterName, master, replica)
}
},
Example: `
#Fetch the logs of the postgres operator
kubectl pg logs -o
#Fetch the logs of the master for provided cluster
kubectl pg logs -c cluster01 -m
#Fetch the logs of the random replica for provided cluster
kubectl pg logs -c cluster01
#Fetch the logs of the provided replica number of the cluster
kubectl pg logs -c cluster01 -r 3
`,
}
func operatorLogs() {
config := getConfig()
client, err := kubernetes.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
operator := getPostgresOperator(client)
allPods, err := client.CoreV1().Pods(operator.Namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
log.Fatal(err)
}
var operatorPodName string
for _, pod := range allPods.Items {
for key, value := range pod.Labels {
if (key == "name" && value == OperatorName) || (key == "app.kubernetes.io/name" && value == OperatorName) {
operatorPodName = pod.Name
break
}
}
}
execRequest := client.CoreV1().RESTClient().Get().Namespace(operator.Namespace).
Name(operatorPodName).
Resource("pods").
SubResource("log").
Param("follow", "--follow").
Param("container", OperatorName)
readCloser, err := execRequest.Stream(context.TODO())
if err != nil {
log.Fatal(err)
}
defer readCloser.Close()
_, err = io.Copy(os.Stdout, readCloser)
if err != nil {
log.Fatal(err)
}
}
func clusterLogs(clusterName string, master bool, replica string) {
config := getConfig()
client, err := kubernetes.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
podName := getPodName(clusterName, master, replica)
execRequest := client.CoreV1().RESTClient().Get().Namespace(getCurrentNamespace()).
Name(podName).
Resource("pods").
SubResource("log").
Param("follow", "--follow").
Param("container", "postgres")
readCloser, err := execRequest.Stream(context.TODO())
if err != nil {
log.Fatal(err)
}
defer readCloser.Close()
_, err = io.Copy(os.Stdout, readCloser)
if err != nil {
log.Fatal(err)
}
}
func init() {
rootCmd.AddCommand(logsCmd)
logsCmd.Flags().BoolP("operator", "o", false, "logs of operator")
logsCmd.Flags().StringP("cluster", "c", "", "logs for the provided cluster")
logsCmd.Flags().BoolP("master", "m", false, "Patroni logs of master")
logsCmd.Flags().StringP("replica", "r", "", "Patroni logs of replica. Specify replica number.")
}

View File

@ -1,51 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var rootCmd = &cobra.Command{
Use: "kubectl-pg",
Short: "kubectl plugin for the Zalando Postgres operator.",
Long: `kubectl pg plugin for interaction with Zalando postgres operator.`,
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func init() {
viper.SetDefault("author", "Vineeth Pothulapati <vineethpothulapati@outlook.com>")
viper.SetDefault("license", "mit")
}

View File

@ -1,194 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"context"
"encoding/json"
"fmt"
"log"
"strconv"
"github.com/spf13/cobra"
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
// scaleCmd represents the scale command
var scaleCmd = &cobra.Command{
Use: "scale",
Short: "Add/remove pods to a Postgres cluster",
Long: `Scales the postgres objects using cluster-name.
Scaling to 0 leads to down time.`,
Run: func(cmd *cobra.Command, args []string) {
clusterName, err := cmd.Flags().GetString("cluster")
if err != nil {
log.Fatal(err)
}
namespace, err := cmd.Flags().GetString("namespace")
if err != nil {
log.Fatal(err)
}
if len(args) > 0 {
numberOfInstances, err := strconv.Atoi(args[0])
if err != nil {
log.Fatal(err)
}
scale(int32(numberOfInstances), clusterName, namespace)
} else {
fmt.Println("Please enter number of instances to scale.")
}
},
Example: `
#Usage
kubectl pg scale [NUMBER-OF-INSTANCES] -c [CLUSTER-NAME] -n [NAMESPACE]
#Scales the number of instances of the provided cluster
kubectl pg scale 5 -c cluster01
`,
}
func scale(numberOfInstances int32, clusterName string, namespace string) {
config := getConfig()
postgresConfig, err := PostgresqlLister.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
postgresql, err := postgresConfig.Postgresqls(namespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
if err != nil {
log.Fatal(err)
}
minInstances, maxInstances := allowedMinMaxInstances(config)
if minInstances == -1 && maxInstances == -1 {
postgresql.Spec.NumberOfInstances = numberOfInstances
} else if numberOfInstances <= maxInstances && numberOfInstances >= minInstances {
postgresql.Spec.NumberOfInstances = numberOfInstances
} else if minInstances == -1 && numberOfInstances < postgresql.Spec.NumberOfInstances ||
maxInstances == -1 && numberOfInstances > postgresql.Spec.NumberOfInstances {
postgresql.Spec.NumberOfInstances = numberOfInstances
} else {
log.Fatalf("cannot scale to the provided instances as they don't adhere to MIN_INSTANCES: %v and MAX_INSTANCES: %v provided in configmap or operatorconfiguration", maxInstances, minInstances)
}
if numberOfInstances == 0 {
fmt.Printf("Scaling to zero leads to down time. please type %s/%s and hit Enter this serves to confirm the action\n", namespace, clusterName)
confirmAction(clusterName, namespace)
}
patchInstances := scalePatch(numberOfInstances)
UpdatedPostgres, err := postgresConfig.Postgresqls(namespace).Patch(context.TODO(), postgresql.Name, types.MergePatchType, patchInstances, metav1.PatchOptions{})
if err != nil {
log.Fatal(err)
}
if UpdatedPostgres.ResourceVersion != postgresql.ResourceVersion {
fmt.Printf("scaled postgresql %s/%s to %d instances\n", UpdatedPostgres.Namespace, UpdatedPostgres.Name, UpdatedPostgres.Spec.NumberOfInstances)
return
}
fmt.Printf("postgresql %s is unchanged.\n", postgresql.Name)
}
func scalePatch(value int32) []byte {
instances := map[string]map[string]int32{"spec": {"numberOfInstances": value}}
patchInstances, err := json.Marshal(instances)
if err != nil {
log.Fatal(err, "unable to parse patch for scale")
}
return patchInstances
}
func allowedMinMaxInstances(config *rest.Config) (int32, int32) {
k8sClient, err := kubernetes.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
operator := getPostgresOperator(k8sClient)
operatorContainer := operator.Spec.Template.Spec.Containers
var configMapName, operatorConfigName string
// -1 indicates no limitations for min/max instances
minInstances := -1
maxInstances := -1
for _, envData := range operatorContainer[0].Env {
if envData.Name == "CONFIG_MAP_NAME" {
configMapName = envData.Value
}
if envData.Name == "POSTGRES_OPERATOR_CONFIGURATION_OBJECT" {
operatorConfigName = envData.Value
}
}
if operatorConfigName == "" {
configMap, err := k8sClient.CoreV1().ConfigMaps(operator.Namespace).Get(context.TODO(), configMapName, metav1.GetOptions{})
if err != nil {
log.Fatal(err)
}
configMapData := configMap.Data
for key, value := range configMapData {
if key == "min_instances" {
minInstances, err = strconv.Atoi(value)
if err != nil {
log.Fatalf("invalid min instances in configmap %v", err)
}
}
if key == "max_instances" {
maxInstances, err = strconv.Atoi(value)
if err != nil {
log.Fatalf("invalid max instances in configmap %v", err)
}
}
}
} else if configMapName == "" {
pgClient, err := PostgresqlLister.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
operatorConfig, err := pgClient.OperatorConfigurations(operator.Namespace).Get(context.TODO(), operatorConfigName, metav1.GetOptions{})
if err != nil {
log.Fatalf("unable to read operator configuration %v", err)
}
minInstances = int(operatorConfig.Configuration.MinInstances)
maxInstances = int(operatorConfig.Configuration.MaxInstances)
}
return int32(minInstances), int32(maxInstances)
}
func init() {
namespace := getCurrentNamespace()
scaleCmd.Flags().StringP("namespace", "n", namespace, "namespace of the cluster to be scaled")
scaleCmd.Flags().StringP("cluster", "c", "", "provide the cluster name.")
rootCmd.AddCommand(scaleCmd)
}

View File

@ -1,96 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"context"
"fmt"
"log"
"os"
"github.com/spf13/cobra"
v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// updateCmd represents kubectl pg update
var updateCmd = &cobra.Command{
Use: "update",
Short: "Updates postgresql object using manifest file",
Long: `Updates the state of cluster using manifest file to reflect the changes on the cluster.`,
Run: func(cmd *cobra.Command, args []string) {
fileName, _ := cmd.Flags().GetString("file")
updatePgResources(fileName)
},
Example: `
#usage
kubectl pg update -f [File-NAME]
#update the postgres cluster with updated manifest file
kubectl pg update -f cluster-manifest.yaml
`,
}
// Update postgresql resources.
func updatePgResources(fileName string) {
config := getConfig()
postgresConfig, err := PostgresqlLister.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
ymlFile, err := os.ReadFile(fileName)
if err != nil {
log.Fatal(err)
}
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode([]byte(ymlFile), nil, &v1.Postgresql{})
if err != nil {
log.Fatal(err)
}
newPostgresObj := obj.(*v1.Postgresql)
oldPostgresObj, err := postgresConfig.Postgresqls(newPostgresObj.Namespace).Get(context.TODO(), newPostgresObj.Name, metav1.GetOptions{})
if err != nil {
log.Fatal(err)
}
newPostgresObj.ResourceVersion = oldPostgresObj.ResourceVersion
response, err := postgresConfig.Postgresqls(newPostgresObj.Namespace).Update(context.TODO(), newPostgresObj, metav1.UpdateOptions{})
if err != nil {
log.Fatal(err)
}
if newPostgresObj.ResourceVersion != response.ResourceVersion {
fmt.Printf("postgresql %s updated.\n", response.Name)
} else {
fmt.Printf("postgresql %s is unchanged.\n", response.Name)
}
}
func init() {
updateCmd.Flags().StringP("file", "f", "", "manifest file with the cluster definition.")
rootCmd.AddCommand(updateCmd)
}

View File

@ -1,172 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"context"
"flag"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
v1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
)
const (
OperatorName = "postgres-operator"
DefaultNamespace = "default"
)
func getConfig() *restclient.Config {
var kubeconfig *string
var config *restclient.Config
envKube := os.Getenv("KUBECONFIG")
if envKube != "" {
kubeconfig = &envKube
} else {
if home := homedir.HomeDir(); home != "" {
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}
}
flag.Parse()
var err error
config, err = clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
log.Fatal(err)
}
return config
}
func getCurrentNamespace() string {
namespace, err := exec.Command("kubectl", "config", "view", "--minify", "--output", "jsonpath={..namespace}").CombinedOutput()
if err != nil {
log.Fatal(err)
}
currentNamespace := string(namespace)
if currentNamespace == "" {
currentNamespace = DefaultNamespace
}
return currentNamespace
}
func confirmAction(clusterName string, namespace string) {
for {
confirmClusterDetails := ""
_, err := fmt.Scan(&confirmClusterDetails)
if err != nil {
log.Fatalf("couldn't get confirmation from the user %v", err)
}
clusterDetails := strings.Split(confirmClusterDetails, "/")
if clusterDetails[0] != namespace || clusterDetails[1] != clusterName {
fmt.Printf("cluster name or namespace does not match. Please re-enter %s/%s\nHint: Press (ctrl+c) to exit\n", namespace, clusterName)
} else {
return
}
}
}
func getPodName(clusterName string, master bool, replicaNumber string) string {
config := getConfig()
client, err := kubernetes.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
postgresConfig, err := PostgresqlLister.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
postgresCluster, err := postgresConfig.Postgresqls(getCurrentNamespace()).Get(context.TODO(), clusterName, metav1.GetOptions{})
if err != nil {
log.Fatal(err)
}
numOfInstances := postgresCluster.Spec.NumberOfInstances
var podName string
var podRole string
replica := clusterName + "-" + replicaNumber
for ins := 0; ins < int(numOfInstances); ins++ {
pod, err := client.CoreV1().Pods(getCurrentNamespace()).Get(context.TODO(), clusterName+"-"+strconv.Itoa(ins), metav1.GetOptions{})
if err != nil {
log.Fatal(err)
}
podRole = pod.Labels["spilo-role"]
if podRole == "master" && master {
podName = pod.Name
fmt.Printf("connected to %s with pod name as %s\n", podRole, podName)
break
} else if podRole == "replica" && !master && (pod.Name == replica || replicaNumber == "") {
podName = pod.Name
fmt.Printf("connected to %s with pod name as %s\n", podRole, podName)
break
}
}
if podName == "" {
log.Fatal("Provided replica doesn't exist")
}
return podName
}
func getPostgresOperator(k8sClient *kubernetes.Clientset) *v1.Deployment {
var operator *v1.Deployment
operator, err := k8sClient.AppsV1().Deployments(getCurrentNamespace()).Get(context.TODO(), OperatorName, metav1.GetOptions{})
if err == nil {
return operator
}
allDeployments := k8sClient.AppsV1().Deployments("")
listDeployments, err := allDeployments.List(context.TODO(), metav1.ListOptions{})
if err != nil {
log.Fatal(err)
}
for _, deployment := range listDeployments.Items {
if deployment.Name == OperatorName {
operator = deployment.DeepCopy()
break
} else {
for key, value := range deployment.Labels {
if key == "app.kubernetes.io/name" && value == OperatorName {
operator = deployment.DeepCopy()
break
}
}
}
}
return operator
}

View File

@ -1,80 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"fmt"
"log"
"strings"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
)
var KubectlPgVersion string = "1.0"
// versionCmd represents the version command
var versionCmd = &cobra.Command{
Use: "version",
Short: "version of kubectl-pg & postgres-operator",
Long: `version of kubectl-pg and current running postgres-operator`,
Run: func(cmd *cobra.Command, args []string) {
namespace, err := cmd.Flags().GetString("namespace")
if err != nil {
log.Fatal(err)
}
version(namespace)
},
Example: `
#Lists the version of kubectl pg plugin and postgres operator in current namespace
kubectl pg version
#Lists the version of kubectl pg plugin and postgres operator in provided namespace
kubectl pg version -n namespace01
`,
}
func version(namespace string) {
fmt.Printf("kubectl-pg: %s\n", KubectlPgVersion)
config := getConfig()
client, err := kubernetes.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
operatorDeployment := getPostgresOperator(client)
if operatorDeployment.Name == "" {
log.Fatalf("make sure zalando's postgres operator is running in namespace %s", namespace)
}
operatorImage := operatorDeployment.Spec.Template.Spec.Containers[0].Image
imageDetails := strings.Split(operatorImage, ":")
imageSplit := len(imageDetails)
imageVersion := imageDetails[imageSplit-1]
fmt.Printf("Postgres-Operator: %s\n", imageVersion)
}
func init() {
rootCmd.AddCommand(versionCmd)
versionCmd.Flags().StringP("namespace", "n", DefaultNamespace, "provide the namespace.")
}

View File

@ -1,72 +0,0 @@
module github.com/zalando/postgres-operator/kubectl-pg
go 1.25.3
require (
github.com/spf13/cobra v1.10.1
github.com/spf13/viper v1.21.0
github.com/zalando/postgres-operator v1.15.0
k8s.io/api v0.32.9
k8s.io/apiextensions-apiserver v0.25.9
k8s.io/apimachinery v0.32.9
k8s.io/client-go v0.32.9
)
require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/time v0.9.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.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/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

View File

@ -1,206 +0,0 @@
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/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/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/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/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=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/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/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d h1:LznySqW8MqVeFh+pW6rOkFdld9QQ7jRydBKKM6jyPVI=
github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d/go.mod h1:u3hJ0kqCQu/cPpsu3RbCOPZ0d7V3IjPjv1adNRleM9I=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
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/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
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/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
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=
github.com/zalando/postgres-operator v1.15.0 h1:is/7cOrpuV7OwMiN7TG7GgiYHKvaWx8Ptw3hJruFO1I=
github.com/zalando/postgres-operator v1.15.0/go.mod h1:1cSOA5dG2dEqdG0uami1RHTGYX92bgAKYASfAhuMtHE=
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.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
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.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
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.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.32.9 h1:q/59kk8lnecgG0grJqzrmXC1Jcl2hPWp9ltz0FQuoLI=
k8s.io/api v0.32.9/go.mod h1:jIfT3rwW4EU1IXZm9qjzSk/2j91k4CJL5vUULrxqp3Y=
k8s.io/apiextensions-apiserver v0.25.9 h1:Pycd6lm2auABp9wKQHCFSEPG+NPdFSTJXPST6NJFzB8=
k8s.io/apiextensions-apiserver v0.25.9/go.mod h1:ijGxmSG1GLOEaWhTuaEr0M7KUeia3mWCZa6FFQqpt1M=
k8s.io/apimachinery v0.32.9 h1:fXk8ktfsxrdThaEOAQFgkhCK7iyoyvS8nbYJ83o/SSs=
k8s.io/apimachinery v0.32.9/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/client-go v0.32.9 h1:ZMyIQ1TEpTDAQni3L2gH1NZzyOA/gHfNcAazzCxMJ0c=
k8s.io/client-go v0.32.9/go.mod h1:2OT8aFSYvUjKGadaeT+AVbhkXQSpMAkiSb88Kz2WggI=
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/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=
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 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

View File

@ -1,31 +0,0 @@
/*
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package main
import (
"github.com/zalando/postgres-operator/kubectl-pg/cmd"
)
func main() {
cmd.Execute()
}