drove-cli 0.0.3__tar.gz → 0.0.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- drove_cli-0.0.5/PKG-INFO +152 -0
- drove_cli-0.0.5/README.md +118 -0
- {drove_cli-0.0.3 → drove_cli-0.0.5}/droveclient.py +11 -4
- drove_cli-0.0.5/plugins/config.py +314 -0
- drove_cli-0.0.5/plugins/describe.py +633 -0
- {drove_cli-0.0.3 → drove_cli-0.0.5}/pyproject.toml +1 -1
- drove_cli-0.0.3/PKG-INFO +0 -1118
- drove_cli-0.0.3/README.md +0 -1084
- {drove_cli-0.0.3 → drove_cli-0.0.5}/drove.py +0 -0
- {drove_cli-0.0.3 → drove_cli-0.0.5}/drovecli.py +0 -0
- {drove_cli-0.0.3 → drove_cli-0.0.5}/droveutils.py +0 -0
- {drove_cli-0.0.3 → drove_cli-0.0.5}/plugins/__init__.py +0 -0
- {drove_cli-0.0.3 → drove_cli-0.0.5}/plugins/appinstances.py +0 -0
- {drove_cli-0.0.3 → drove_cli-0.0.5}/plugins/applications.py +0 -0
- {drove_cli-0.0.3 → drove_cli-0.0.5}/plugins/cluster.py +0 -0
- {drove_cli-0.0.3 → drove_cli-0.0.5}/plugins/executors.py +0 -0
- {drove_cli-0.0.3 → drove_cli-0.0.5}/plugins/localserviceinstances.py +0 -0
- {drove_cli-0.0.3 → drove_cli-0.0.5}/plugins/localservices.py +0 -0
- {drove_cli-0.0.3 → drove_cli-0.0.5}/plugins/tasks.py +0 -0
drove_cli-0.0.5/PKG-INFO
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: drove-cli
|
|
3
|
+
Version: 0.0.5
|
|
4
|
+
Summary: Drove Command Line Interface
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Keywords: container,docker,podman,distributed-systems,container-orchestrator
|
|
7
|
+
Author: Santanu Sinha
|
|
8
|
+
Author-email: santanu.sinha@gmail.com
|
|
9
|
+
Maintainer: Santanu Sinha
|
|
10
|
+
Maintainer-email: santanu.sinha@gmail.com
|
|
11
|
+
Requires-Python: >=3.7,<4.0
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Topic :: System :: Clustering
|
|
26
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
27
|
+
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
28
|
+
Requires-Dist: tabulate (>=0.9.0,<0.10.0)
|
|
29
|
+
Requires-Dist: tenacity (>=8.2.3,<9.0.0)
|
|
30
|
+
Requires-Dist: urllib3 (>=2.0.7,<3.0.0)
|
|
31
|
+
Project-URL: Repository, https://github.com/PhonePe/drove-cli
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# Drove CLI
|
|
35
|
+
|
|
36
|
+
Command-line interface for the [Drove Container Orchestrator](https://github.com/PhonePe/drove-orchestrator).
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
### Using pip
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install drove-cli
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Using pip (virtual environment)
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
python3 -m venv ~/.venvs/drove-cli
|
|
50
|
+
source ~/.venvs/drove-cli/bin/activate
|
|
51
|
+
pip install drove-cli
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
To activate in a new shell:
|
|
55
|
+
```bash
|
|
56
|
+
source ~/.venvs/drove-cli/bin/activate
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Using Docker
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
docker pull ghcr.io/phonepe/drove-cli:latest
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Create a wrapper script for convenience:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
cat > ~/bin/drove << 'EOF'
|
|
69
|
+
#!/bin/sh
|
|
70
|
+
docker run --rm -it --network host \
|
|
71
|
+
-v ${HOME}/.drove:/root/.drove:ro \
|
|
72
|
+
ghcr.io/phonepe/drove-cli:latest "$@"
|
|
73
|
+
EOF
|
|
74
|
+
chmod +x ~/bin/drove
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Configuration
|
|
78
|
+
|
|
79
|
+
Create `~/.drove` with your cluster configuration:
|
|
80
|
+
|
|
81
|
+
```ini
|
|
82
|
+
[DEFAULT]
|
|
83
|
+
stage_token = <your-stage-token>
|
|
84
|
+
prod_token = <your-prod-token>
|
|
85
|
+
|
|
86
|
+
[local]
|
|
87
|
+
endpoint = http://localhost:10000
|
|
88
|
+
username = admin
|
|
89
|
+
password = admin
|
|
90
|
+
|
|
91
|
+
[stage]
|
|
92
|
+
endpoint = https://drove.stage.example.com
|
|
93
|
+
auth_header = %(stage_token)s
|
|
94
|
+
|
|
95
|
+
[prod]
|
|
96
|
+
endpoint = https://drove.prod.example.com
|
|
97
|
+
auth_header = %(prod_token)s
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Quick Start
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Verify connection
|
|
104
|
+
drove -c prod cluster ping
|
|
105
|
+
|
|
106
|
+
# View cluster status
|
|
107
|
+
drove -c prod cluster summary
|
|
108
|
+
|
|
109
|
+
# List applications
|
|
110
|
+
drove -c prod apps list
|
|
111
|
+
|
|
112
|
+
# Get application info
|
|
113
|
+
drove -c prod apps info <app-name>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Commands
|
|
117
|
+
|
|
118
|
+
| Command | Description |
|
|
119
|
+
|---------|-----------------------------------------------------------------------|
|
|
120
|
+
| `appinstances` | Application instance operations |
|
|
121
|
+
| `apps` | Application lifecycle Management (list, info, deploy, scale, suspend) |
|
|
122
|
+
| `cluster` | Cluster operations (ping, summary, leader, maintenance) |
|
|
123
|
+
| `config` | CLI configuration management |
|
|
124
|
+
| `describe` | Show detailed information about a resource |
|
|
125
|
+
| `executor` | Executor management |
|
|
126
|
+
| `localservices` | Local service management |
|
|
127
|
+
| `lsinstances` | Local service instance operations |
|
|
128
|
+
| `tasks` | One-off task execution |
|
|
129
|
+
|
|
130
|
+
Use `drove -h` or `drove <command> -h` for detailed help.
|
|
131
|
+
|
|
132
|
+
## Global Options
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
-f, --file FILE Configuration file (default: ~/.drove)
|
|
136
|
+
-c, --cluster CLUSTER Cluster name from config file
|
|
137
|
+
-e, --endpoint URL Drove endpoint URL
|
|
138
|
+
-t, --auth-header HDR Authorization header value
|
|
139
|
+
-u, --username USER Cluster username
|
|
140
|
+
-p, --password PASS Cluster password
|
|
141
|
+
-i, --insecure Skip SSL verification
|
|
142
|
+
-d, --debug Print error details
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Documentation
|
|
146
|
+
|
|
147
|
+
Full documentation is available at **[github.com/PhonePe/drove-orchestrator](https://github.com/PhonePe/drove-orchestrator)**
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
© 2024 Santanu Sinha | Apache 2.0
|
|
152
|
+
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Drove CLI
|
|
2
|
+
|
|
3
|
+
Command-line interface for the [Drove Container Orchestrator](https://github.com/PhonePe/drove-orchestrator).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Using pip
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install drove-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Using pip (virtual environment)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
python3 -m venv ~/.venvs/drove-cli
|
|
17
|
+
source ~/.venvs/drove-cli/bin/activate
|
|
18
|
+
pip install drove-cli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
To activate in a new shell:
|
|
22
|
+
```bash
|
|
23
|
+
source ~/.venvs/drove-cli/bin/activate
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Using Docker
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
docker pull ghcr.io/phonepe/drove-cli:latest
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Create a wrapper script for convenience:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cat > ~/bin/drove << 'EOF'
|
|
36
|
+
#!/bin/sh
|
|
37
|
+
docker run --rm -it --network host \
|
|
38
|
+
-v ${HOME}/.drove:/root/.drove:ro \
|
|
39
|
+
ghcr.io/phonepe/drove-cli:latest "$@"
|
|
40
|
+
EOF
|
|
41
|
+
chmod +x ~/bin/drove
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Configuration
|
|
45
|
+
|
|
46
|
+
Create `~/.drove` with your cluster configuration:
|
|
47
|
+
|
|
48
|
+
```ini
|
|
49
|
+
[DEFAULT]
|
|
50
|
+
stage_token = <your-stage-token>
|
|
51
|
+
prod_token = <your-prod-token>
|
|
52
|
+
|
|
53
|
+
[local]
|
|
54
|
+
endpoint = http://localhost:10000
|
|
55
|
+
username = admin
|
|
56
|
+
password = admin
|
|
57
|
+
|
|
58
|
+
[stage]
|
|
59
|
+
endpoint = https://drove.stage.example.com
|
|
60
|
+
auth_header = %(stage_token)s
|
|
61
|
+
|
|
62
|
+
[prod]
|
|
63
|
+
endpoint = https://drove.prod.example.com
|
|
64
|
+
auth_header = %(prod_token)s
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Quick Start
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Verify connection
|
|
71
|
+
drove -c prod cluster ping
|
|
72
|
+
|
|
73
|
+
# View cluster status
|
|
74
|
+
drove -c prod cluster summary
|
|
75
|
+
|
|
76
|
+
# List applications
|
|
77
|
+
drove -c prod apps list
|
|
78
|
+
|
|
79
|
+
# Get application info
|
|
80
|
+
drove -c prod apps info <app-name>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Commands
|
|
84
|
+
|
|
85
|
+
| Command | Description |
|
|
86
|
+
|---------|-----------------------------------------------------------------------|
|
|
87
|
+
| `appinstances` | Application instance operations |
|
|
88
|
+
| `apps` | Application lifecycle Management (list, info, deploy, scale, suspend) |
|
|
89
|
+
| `cluster` | Cluster operations (ping, summary, leader, maintenance) |
|
|
90
|
+
| `config` | CLI configuration management |
|
|
91
|
+
| `describe` | Show detailed information about a resource |
|
|
92
|
+
| `executor` | Executor management |
|
|
93
|
+
| `localservices` | Local service management |
|
|
94
|
+
| `lsinstances` | Local service instance operations |
|
|
95
|
+
| `tasks` | One-off task execution |
|
|
96
|
+
|
|
97
|
+
Use `drove -h` or `drove <command> -h` for detailed help.
|
|
98
|
+
|
|
99
|
+
## Global Options
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
-f, --file FILE Configuration file (default: ~/.drove)
|
|
103
|
+
-c, --cluster CLUSTER Cluster name from config file
|
|
104
|
+
-e, --endpoint URL Drove endpoint URL
|
|
105
|
+
-t, --auth-header HDR Authorization header value
|
|
106
|
+
-u, --username USER Cluster username
|
|
107
|
+
-p, --password PASS Cluster password
|
|
108
|
+
-i, --insecure Skip SSL verification
|
|
109
|
+
-d, --debug Print error details
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Documentation
|
|
113
|
+
|
|
114
|
+
Full documentation is available at **[github.com/PhonePe/drove-orchestrator](https://github.com/PhonePe/drove-orchestrator)**
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
© 2024 Santanu Sinha | Apache 2.0
|
|
@@ -173,12 +173,19 @@ def build_drove_client(drove_client: DroveClient, args: SimpleNamespace):
|
|
|
173
173
|
with open(config_file) as stream:
|
|
174
174
|
config_parser.read_string(stream.read())
|
|
175
175
|
drove_config = config_parser['DEFAULT']
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
176
|
+
|
|
177
|
+
# Determine which cluster to use (priority: -c flag > current_cluster in config > DEFAULT)
|
|
178
|
+
cluster_to_use = args.cluster
|
|
179
|
+
if cluster_to_use is None:
|
|
180
|
+
cluster_to_use = config_parser.defaults().get('current_cluster')
|
|
181
|
+
|
|
182
|
+
if cluster_to_use is not None and cluster_to_use != 'DEFAULT':
|
|
183
|
+
if cluster_to_use in config_parser:
|
|
184
|
+
drove_config = config_parser[cluster_to_use]
|
|
179
185
|
else:
|
|
180
|
-
print("error: No cluster definition found for {cluster} in config {config_file}".format(config_file=config_file, cluster=
|
|
186
|
+
print("error: No cluster definition found for {cluster} in config {config_file}".format(config_file=config_file, cluster=cluster_to_use))
|
|
181
187
|
return None
|
|
188
|
+
|
|
182
189
|
endpoint = drove_config.get("endpoint")
|
|
183
190
|
username = drove_config.get("username")
|
|
184
191
|
password = drove_config.get("password")
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Config plugin for drove-cli - cluster configuration management.
|
|
3
|
+
|
|
4
|
+
This plugin adds 'drove config' commands for managing clusters, largely inspired by kubectl context management.
|
|
5
|
+
- drove config current-cluster : Show current default cluster
|
|
6
|
+
- drove config get-clusters : List all configured clusters
|
|
7
|
+
- drove config use-cluster <name> : Set default cluster
|
|
8
|
+
- drove config view : Show full config file
|
|
9
|
+
- drove config init : Initialize a new config file
|
|
10
|
+
- drove config add-cluster <name> : Add a new cluster to config
|
|
11
|
+
- drove config delete-cluster <name> : Remove a cluster from config
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
drove config use-cluster local
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import configparser
|
|
20
|
+
import os
|
|
21
|
+
import plugins
|
|
22
|
+
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from types import SimpleNamespace
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_config_file_path() -> str:
|
|
28
|
+
return str(Path.home()) + "/.drove"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def read_config() -> configparser.ConfigParser:
|
|
32
|
+
config_file = get_config_file_path()
|
|
33
|
+
config = configparser.ConfigParser()
|
|
34
|
+
|
|
35
|
+
if os.path.isfile(config_file) and os.access(config_file, os.R_OK):
|
|
36
|
+
with open(config_file) as stream:
|
|
37
|
+
config.read_string(stream.read())
|
|
38
|
+
|
|
39
|
+
return config
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def write_config(config: configparser.ConfigParser) -> None:
|
|
43
|
+
config_file = get_config_file_path()
|
|
44
|
+
with open(config_file, 'w') as f:
|
|
45
|
+
config.write(f)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_current_cluster() -> str:
|
|
49
|
+
config = read_config()
|
|
50
|
+
return config.defaults().get('current_cluster')
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def set_current_cluster(cluster_name: str) -> None:
|
|
54
|
+
config = read_config()
|
|
55
|
+
config.set('DEFAULT', 'current_cluster', cluster_name)
|
|
56
|
+
write_config(config)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_clusters_from_config(config: configparser.ConfigParser) -> list:
|
|
60
|
+
clusters = []
|
|
61
|
+
for section in config.sections():
|
|
62
|
+
endpoint = config[section].get('endpoint', '')
|
|
63
|
+
if endpoint:
|
|
64
|
+
clusters.append({
|
|
65
|
+
'name': section,
|
|
66
|
+
'endpoint': endpoint,
|
|
67
|
+
'has_auth': bool(config[section].get('username') or config[section].get('auth_header')),
|
|
68
|
+
'insecure': config[section].get('insecure', 'false').lower() == 'true'
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
if config.defaults().get('endpoint'):
|
|
72
|
+
clusters.insert(0, {
|
|
73
|
+
'name': 'DEFAULT',
|
|
74
|
+
'endpoint': config.defaults().get('endpoint'),
|
|
75
|
+
'has_auth': bool(config.defaults().get('username') or config.defaults().get('auth_header')),
|
|
76
|
+
'insecure': config.defaults().get('insecure', 'false').lower() == 'true'
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
return clusters
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class Config(plugins.DrovePlugin):
|
|
83
|
+
"""Plugin for config/context management commands."""
|
|
84
|
+
|
|
85
|
+
def __init__(self) -> None:
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
def populate_options(self, drove_client, subparser: argparse.ArgumentParser):
|
|
89
|
+
parser = subparser.add_parser("config", help="Manage drove cluster configurations")
|
|
90
|
+
commands = parser.add_subparsers(help="Available config commands")
|
|
91
|
+
|
|
92
|
+
# get-clusters: List all available clusters
|
|
93
|
+
sub_parser = commands.add_parser("get-clusters", help="List all configured clusters")
|
|
94
|
+
sub_parser.set_defaults(func=self.get_clusters)
|
|
95
|
+
|
|
96
|
+
# current-cluster: Show current default
|
|
97
|
+
sub_parser = commands.add_parser("current-cluster", help="Show current default cluster")
|
|
98
|
+
sub_parser.set_defaults(func=self.current_cluster)
|
|
99
|
+
|
|
100
|
+
# use-cluster: Set default cluster
|
|
101
|
+
sub_parser = commands.add_parser("use-cluster", help="Set default cluster")
|
|
102
|
+
sub_parser.add_argument("cluster_name", metavar="cluster-name", help="Cluster name to use as default")
|
|
103
|
+
sub_parser.set_defaults(func=self.use_cluster)
|
|
104
|
+
|
|
105
|
+
# view: Show full config
|
|
106
|
+
sub_parser = commands.add_parser("view", help="Display the full configuration file")
|
|
107
|
+
sub_parser.add_argument("--raw", "-r", help="Show raw config file content", action="store_true")
|
|
108
|
+
sub_parser.set_defaults(func=self.view_config)
|
|
109
|
+
|
|
110
|
+
# init: Initialize config file
|
|
111
|
+
sub_parser = commands.add_parser("init", help="Initialize a new ~/.drove config file")
|
|
112
|
+
sub_parser.add_argument("--endpoint", "-e", help="Drove endpoint URL", required=True)
|
|
113
|
+
sub_parser.add_argument("--name", "-n", help="Cluster name", default="default")
|
|
114
|
+
sub_parser.add_argument("--username", "-u", help="Username for basic auth")
|
|
115
|
+
sub_parser.add_argument("--password", "-p", help="Password for basic auth")
|
|
116
|
+
sub_parser.add_argument("--auth-header", "-t", dest="auth_header", help="Authorization header value")
|
|
117
|
+
sub_parser.add_argument("--insecure", "-i", help="Skip SSL verification", action="store_true")
|
|
118
|
+
sub_parser.set_defaults(func=self.init_config)
|
|
119
|
+
|
|
120
|
+
# add-cluster: Add a new cluster to config
|
|
121
|
+
sub_parser = commands.add_parser("add-cluster", help="Add a new cluster to config")
|
|
122
|
+
sub_parser.add_argument("cluster_name", metavar="cluster-name", help="Name for this cluster")
|
|
123
|
+
sub_parser.add_argument("--endpoint", "-e", help="Drove endpoint URL", required=True)
|
|
124
|
+
sub_parser.add_argument("--username", "-u", help="Username for basic auth")
|
|
125
|
+
sub_parser.add_argument("--password", "-p", help="Password for basic auth")
|
|
126
|
+
sub_parser.add_argument("--auth-header", "-t", dest="auth_header", help="Authorization header value")
|
|
127
|
+
sub_parser.add_argument("--insecure", "-i", help="Skip SSL verification", action="store_true")
|
|
128
|
+
sub_parser.set_defaults(func=self.add_cluster)
|
|
129
|
+
|
|
130
|
+
# delete-cluster: Remove a cluster from config
|
|
131
|
+
sub_parser = commands.add_parser("delete-cluster", help="Remove a cluster from config")
|
|
132
|
+
sub_parser.add_argument("cluster_name", metavar="cluster-name", help="Cluster name to remove")
|
|
133
|
+
sub_parser.set_defaults(func=self.delete_cluster)
|
|
134
|
+
|
|
135
|
+
super().populate_options(drove_client, parser)
|
|
136
|
+
|
|
137
|
+
def get_clusters(self, options: SimpleNamespace):
|
|
138
|
+
config = read_config()
|
|
139
|
+
clusters = get_clusters_from_config(config)
|
|
140
|
+
current = get_current_cluster()
|
|
141
|
+
|
|
142
|
+
if not clusters:
|
|
143
|
+
print("No clusters configured. Run 'drove config init' to set up.")
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
print(f"{'CURRENT':<10} {'NAME':<20} {'ENDPOINT':<50} {'AUTH':<6} {'INSECURE':<8}")
|
|
147
|
+
print("-" * 95)
|
|
148
|
+
|
|
149
|
+
for cluster in clusters:
|
|
150
|
+
marker = "*" if cluster['name'] == current else ""
|
|
151
|
+
auth = "yes" if cluster['has_auth'] else "no"
|
|
152
|
+
insecure = "yes" if cluster['insecure'] else "no"
|
|
153
|
+
print(f"{marker:<10} {cluster['name']:<20} {cluster['endpoint']:<50} {auth:<6} {insecure:<8}")
|
|
154
|
+
|
|
155
|
+
if current:
|
|
156
|
+
print(f"\nCurrent cluster: {current}")
|
|
157
|
+
|
|
158
|
+
def current_cluster(self, options: SimpleNamespace):
|
|
159
|
+
current = get_current_cluster()
|
|
160
|
+
if current:
|
|
161
|
+
print(current)
|
|
162
|
+
else:
|
|
163
|
+
print("No current cluster set. Use 'drove config use-cluster <name>' to set one.")
|
|
164
|
+
|
|
165
|
+
def use_cluster(self, options: SimpleNamespace):
|
|
166
|
+
config = read_config()
|
|
167
|
+
cluster_name = options.cluster_name
|
|
168
|
+
|
|
169
|
+
# Validate cluster exists
|
|
170
|
+
valid_names = [s for s in config.sections()]
|
|
171
|
+
if cluster_name == 'DEFAULT':
|
|
172
|
+
if config.defaults().get('endpoint'):
|
|
173
|
+
valid_names.append('DEFAULT')
|
|
174
|
+
|
|
175
|
+
if cluster_name not in valid_names and cluster_name != 'DEFAULT':
|
|
176
|
+
print(f"Error: cluster '{cluster_name}' not found in config")
|
|
177
|
+
print(f"Available clusters: {', '.join(valid_names) if valid_names else 'none'}")
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
set_current_cluster(cluster_name)
|
|
181
|
+
print(f"Switched to cluster \"{cluster_name}\".")
|
|
182
|
+
|
|
183
|
+
def view_config(self, options: SimpleNamespace):
|
|
184
|
+
config_file = get_config_file_path()
|
|
185
|
+
|
|
186
|
+
if not os.path.isfile(config_file):
|
|
187
|
+
print(f"Config file not found: {config_file}")
|
|
188
|
+
print("Run 'drove config init' to create one.")
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
if options.raw:
|
|
192
|
+
with open(config_file, 'r') as f:
|
|
193
|
+
print(f.read())
|
|
194
|
+
else:
|
|
195
|
+
config = read_config()
|
|
196
|
+
current = get_current_cluster()
|
|
197
|
+
|
|
198
|
+
print(f"Config file: {config_file}")
|
|
199
|
+
print(f"Current cluster: {current or '(not set)'}")
|
|
200
|
+
print()
|
|
201
|
+
|
|
202
|
+
for section in ['DEFAULT'] + config.sections():
|
|
203
|
+
if section == 'DEFAULT':
|
|
204
|
+
items = dict(config.defaults())
|
|
205
|
+
else:
|
|
206
|
+
items = dict(config[section])
|
|
207
|
+
|
|
208
|
+
if not items.get('endpoint'):
|
|
209
|
+
continue
|
|
210
|
+
|
|
211
|
+
marker = " (current)" if section == current else ""
|
|
212
|
+
print(f"[{section}]{marker}")
|
|
213
|
+
for key, value in items.items():
|
|
214
|
+
# Mask sensitive values
|
|
215
|
+
if key in ('password', 'auth_header') and value:
|
|
216
|
+
display_value = value[:4] + "****" + value[-4:] if len(value) > 8 else "****"
|
|
217
|
+
else:
|
|
218
|
+
display_value = value
|
|
219
|
+
print(f" {key} = {display_value}")
|
|
220
|
+
print()
|
|
221
|
+
|
|
222
|
+
def init_config(self, options: SimpleNamespace):
|
|
223
|
+
config_file = get_config_file_path()
|
|
224
|
+
|
|
225
|
+
if os.path.isfile(config_file):
|
|
226
|
+
print(f"Config file already exists: {config_file}")
|
|
227
|
+
print("Use 'drove config add-context' to add more clusters.")
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
config = configparser.ConfigParser()
|
|
231
|
+
section = options.name if options.name != 'default' else 'DEFAULT'
|
|
232
|
+
|
|
233
|
+
if section != 'DEFAULT':
|
|
234
|
+
config.add_section(section)
|
|
235
|
+
config[section]
|
|
236
|
+
else:
|
|
237
|
+
config.defaults()
|
|
238
|
+
|
|
239
|
+
if section == 'DEFAULT':
|
|
240
|
+
config.set('DEFAULT', 'endpoint', options.endpoint)
|
|
241
|
+
if options.username:
|
|
242
|
+
config.set('DEFAULT', 'username', options.username)
|
|
243
|
+
if options.password:
|
|
244
|
+
config.set('DEFAULT', 'password', options.password)
|
|
245
|
+
if options.auth_header:
|
|
246
|
+
config.set('DEFAULT', 'auth_header', options.auth_header)
|
|
247
|
+
if options.insecure:
|
|
248
|
+
config.set('DEFAULT', 'insecure', 'true')
|
|
249
|
+
else:
|
|
250
|
+
config[section]['endpoint'] = options.endpoint
|
|
251
|
+
if options.username:
|
|
252
|
+
config[section]['username'] = options.username
|
|
253
|
+
if options.password:
|
|
254
|
+
config[section]['password'] = options.password
|
|
255
|
+
if options.auth_header:
|
|
256
|
+
config[section]['auth_header'] = options.auth_header
|
|
257
|
+
if options.insecure:
|
|
258
|
+
config[section]['insecure'] = 'true'
|
|
259
|
+
|
|
260
|
+
with open(config_file, 'w') as f:
|
|
261
|
+
config.write(f)
|
|
262
|
+
|
|
263
|
+
set_current_cluster(section)
|
|
264
|
+
|
|
265
|
+
print(f"Config initialized at: {config_file}")
|
|
266
|
+
print(f"Current cluster set to: {section}")
|
|
267
|
+
|
|
268
|
+
def add_cluster(self, options: SimpleNamespace):
|
|
269
|
+
"""Add a new cluster."""
|
|
270
|
+
config = read_config()
|
|
271
|
+
cluster_name = options.cluster_name
|
|
272
|
+
|
|
273
|
+
if cluster_name in config.sections():
|
|
274
|
+
print(f"Error: cluster '{cluster_name}' already exists")
|
|
275
|
+
return
|
|
276
|
+
|
|
277
|
+
config.add_section(cluster_name)
|
|
278
|
+
config[cluster_name]['endpoint'] = options.endpoint
|
|
279
|
+
if options.username:
|
|
280
|
+
config[cluster_name]['username'] = options.username
|
|
281
|
+
if options.password:
|
|
282
|
+
config[cluster_name]['password'] = options.password
|
|
283
|
+
if options.auth_header:
|
|
284
|
+
config[cluster_name]['auth_header'] = options.auth_header
|
|
285
|
+
if options.insecure:
|
|
286
|
+
config[cluster_name]['insecure'] = 'true'
|
|
287
|
+
|
|
288
|
+
config_file = get_config_file_path()
|
|
289
|
+
with open(config_file, 'w') as f:
|
|
290
|
+
config.write(f)
|
|
291
|
+
|
|
292
|
+
print(f"Cluster '{cluster_name}' added to {config_file}")
|
|
293
|
+
|
|
294
|
+
def delete_cluster(self, options: SimpleNamespace):
|
|
295
|
+
config = read_config()
|
|
296
|
+
cluster_name = options.cluster_name
|
|
297
|
+
|
|
298
|
+
if cluster_name not in config.sections():
|
|
299
|
+
print(f"Error: cluster '{cluster_name}' not found")
|
|
300
|
+
return
|
|
301
|
+
|
|
302
|
+
config.remove_section(cluster_name)
|
|
303
|
+
|
|
304
|
+
config_file = get_config_file_path()
|
|
305
|
+
with open(config_file, 'w') as f:
|
|
306
|
+
config.write(f)
|
|
307
|
+
|
|
308
|
+
current = get_current_cluster()
|
|
309
|
+
if current == cluster_name:
|
|
310
|
+
config.remove_option('DEFAULT', 'current_cluster')
|
|
311
|
+
write_config(config)
|
|
312
|
+
print(f"Cluster '{cluster_name}' deleted and was current cluster (now unset)")
|
|
313
|
+
else:
|
|
314
|
+
print(f"Cluster '{cluster_name}' deleted from {config_file}")
|