otoroshictl 0.0.5

a CLI to manage your otoroshi clusters with style ;)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
use clap::{Parser, Subcommand};
use std::path::PathBuf;

#[derive(Parser, Clone, Debug)]
#[clap(name = "otoroshictl")]
#[clap(author = "Mathieu ANCELIN (Cloud APIM)")]
#[clap(version = "1.0.0")]
#[clap(about = "Manage your otoroshi clusters with style. otoroshictl is a CLI that can interact with the admin api of an otoroshi cluster. \n\nYou can also use it to expose local process through the otoroshi remote tunnels feature and as an universal sidecar to create a service mesh based on otoroshi. otoroshictl also provide a nice integration with cloud-apim, the managed otoroshi instances provider. \n\notoroshictl is an open-source tool proudly provided by cloud-apim (https://www.cloud-apim.com). The sources of otoroshictl are available on github at https://github.com/cloud-apim/otoroshictl", long_about = None)]
#[clap(arg_required_else_help(true))]
pub struct CliOpts {

    /// Turn debugging information on
    #[arg(short, long, global = true, action = clap::ArgAction::SetTrue)]
    pub verbose: bool,

    /// Change the rendering format (can be one of: json, yaml, json_pretty)
    #[arg(short, long, global = true, value_name = "FORMAT")]
    pub ouput: Option<String>,

    /// Sets a custom config file
    #[arg(short, long, value_name = "FILE or URL", global = true)]
    pub config_file: Option<String>,

    #[command(subcommand)]
    pub command: Option<Commands>,

    /// Sets the tls flag to connect to a custom otoroshi cluster without using a config file
    #[arg(long, global = true, action = clap::ArgAction::SetTrue)]
    pub otoroshi_cluster_tls: bool,
    /// Sets the hostname to connect to a custom otoroshi cluster without using a config file
    #[arg(long, global = true, value_name = "HOSTNAME")]
    pub otoroshi_cluster_hostname: Option<String>,
    /// Sets the port to connect to a custom otoroshi cluster without using a config file
    #[arg(long, global = true, value_name = "PORT")]
    pub otoroshi_cluster_port: Option<u16>,
     /// Sets the tls flag to connect to a custom otoroshi cluster without using a config file
     #[arg(long, global = true, action = clap::ArgAction::SetTrue)]
     pub otoroshi_cluster_routing_tls: Option<bool>,
     /// Sets the hostname to connect to a custom otoroshi cluster without using a config file
     #[arg(long, global = true, value_name = "HOSTNAME")]
     pub otoroshi_cluster_routing_hostname: Option<String>,
     /// Sets the port to connect to a custom otoroshi cluster without using a config file
     #[arg(long, global = true, value_name = "PORT")]
     pub otoroshi_cluster_routing_port: Option<u16>,
    /// Sets the client_id to connect to a custom otoroshi cluster without using a config file
    #[arg(long, global = true, value_name = "CLIENT_ID")]
    pub otoroshi_user_client_id: Option<String>,
    /// Sets the client_secret to connect to a custom otoroshi cluster without using a config file
    #[arg(long, global = true, value_name = "CLIENT_SECRET")]
    pub otoroshi_user_client_secret: Option<String>,
    /// Sets the health_key to connect to a custom otoroshi cluster without using a config file
    #[arg(long, global = true, value_name = "HEALTH_KEY")]
    pub otoroshi_user_health_key: Option<String>,
    /// Sets the client cert location to connect to a custom otoroshi cluster without using a config file
    #[arg(long, global = true, value_name = "FILE")]
    pub otoroshi_cluster_cert_location: Option<String>,
    /// Sets the client cert key location to connect to a custom otoroshi cluster without using a config file
    #[arg(long, global = true, value_name = "FILE")]
    pub otoroshi_cluster_key_location: Option<String>,
    /// Sets the client cert ca location to connect to a custom otoroshi cluster without using a config file
    #[arg(long, global = true, value_name = "FILE")]
    pub otoroshi_cluster_ca_location: Option<String>,
}

#[derive(Subcommand, Clone, Debug)]
pub enum ConfigSubCommand {
    /// Display the current config. file content
    CurrentConfig {},
    /// Edit the current config. file
    EditCurrentConfig {},
    /// Display current config. location
    CurrentLocation {},
    /// Display current context
    CurrentContext {},
    /// Set the current context
    UseContext {
        /// Name of the context
        name: String,
    },
    /// Set the current context
    Use {
        /// Name of the context
        name: String,
    },
    /// Rename a context
    RenameContext {
        /// Name of the context
        old_name: String,
        /// New name of the context
        new_name: String,
    },
    /// Display the list of usable contexts
    List {},
    /// Display the list of clusters
    ListClusters {},
    /// Display the list of users
    ListUsers {},
    /// Display the list of contexts
    ListContexts {},
    /// Create or update a cluster
    SetCluster {
        /// Name of the cluster
        name: String,
        /// hostname of the cluster api
        #[arg(long)]
        hostname: String,
        /// port of the cluster api
        #[arg(long)]
        port: u16,
        /// does the cluster api uses tls
        #[arg(long, action = clap::ArgAction::SetTrue)]
        tls: bool,
        /// hostname used for routing (sidecar only)
        #[arg(long)]
        routing_hostname: Option<String>,
        /// port used for routing (sidecar only)
        #[arg(long)]
        routing_port: Option<u16>,
        /// tls flag used for routing (sidecar only)
        #[arg(long, action = clap::ArgAction::SetTrue)]
        routing_tls: Option<bool>,
    },
    /// Create or update a user
    SetUser {
        /// Name of the cluster
        name: String,
        /// client_id of the cluster api
        #[arg(long)]
        client_id: String,
        /// client_secret of the cluster api
        #[arg(long)]
        client_secret: String,
        /// health_access_key of the cluster for health and metrics
        #[arg(long)]
        health_key: Option<String>,
    },
    /// Create or update a context
    SetContext {
        /// Name of the cluster
        name: String,
        /// cluster name for this context
        #[arg(long)]
        cluster: String,
        /// user name for this context
        #[arg(long)]
        user: String,
    },
    /// Create and set a full config
    Add {
        /// Name of the cluster
        name: String,
        /// client_id of the cluster api
        #[arg(long)]
        client_id: String,
        /// client_secret of the cluster api
        #[arg(long)]
        client_secret: String,
        /// health_access_key of the cluster for health and metrics
        #[arg(long)]
        health_key: Option<String>,
        /// hostname of the cluster api
        #[arg(long)]
        hostname: String,
        /// port of the cluster api
        #[arg(long)]
        port: u16,
        /// does the cluster api uses tls
        #[arg(long, action = clap::ArgAction::SetTrue)]
        tls: bool,
        /// Change current context for this one
        #[arg(long, action = clap::ArgAction::SetTrue)]
        current: bool,
        /// hostname used for routing (sidecar only)
        #[arg(long)]
        routing_hostname: Option<String>,
        /// port used for routing (sidecar only)
        #[arg(long)]
        routing_port: Option<u16>,
        /// tls flag used for routing (sidecar only)
        #[arg(long, action = clap::ArgAction::SetTrue)]
        routing_tls: Option<bool>,
    },
    /// Delete a cluster
    DeleteCluster {
        /// Name of the cluster
        name: String,
    },
    /// Delete a user
    DeleteUser {
        /// Name of the user
        name: String,
    },
    /// Delete a context
    DeleteContext {
        /// Name of the context
        name: String,
    },
    /// Delete a full context with the associated cluster and user
    Delete {
        /// Name of the context
        name: Option<String>,
    },
    /// Delete configuration and start with a clean one
    Reset {},
    /// Import a context file with current context file
    Import {
        /// The name of the context you want to import
        #[arg(short, long, value_name = "NAME")]
        name: Option<String>,
        /// Change current context to imported one
        #[arg(long, action = clap::ArgAction::SetTrue)]
        current: bool,
        /// Overwrite
        #[arg(long, action = clap::ArgAction::SetTrue)]
        overwrite: bool,
        /// The file or url containing the json object to merge
        file: String,
    },
}

#[derive(Subcommand, Clone, Debug)]
pub enum ResourcesSubCommand {
    /// Generate a template for the current kind
    Template {
        /// Resource name to operate on
        resource: String,
        /// Add kube armor to resources
        #[arg(short, long, action = clap::ArgAction::SetTrue)]
        kube: Option<bool>,
    },
    /// Generate crds manifest for kubernetes
    Crds {
        #[arg(short, long, value_name = "FILE")] // ok just file because writing
        file: Option<PathBuf>,
    },
    /// Generate rbac manifest for kubernetes       
    Rbac {
        #[arg(short, long, value_name = "FILE")] // ok just file because writing
        file: Option<PathBuf>,
    }, 
    /// Get otoroshi resource from current cluster
    Get {
        /// Optional resource name to operate on
        resource: Option<String>,
        /// Optional resource id to operate on
        id: Option<String>,
        /// Optional comma separated list of columns to display
        #[arg(long, global = true)]
        columns: Vec<String>,
        /// Add kube armor to resources
        #[arg(short, long, action = clap::ArgAction::SetTrue)]
        kube: Option<bool>,
        /// The viewed page
        #[arg(long)]
        page: Option<u32>,
        /// The viewed page size
        #[arg(long)]
        page_size: Option<u32>,
        /// Filter the returned elements
        #[arg(short, long)]
        filters: Vec<String>,
    },
    /// Delete otoroshi resources
    Delete {
        /// Optional resource name to operate on
        resource: Option<String>,
        /// the ids to delete
        ids: Vec<String>,
        /// The file to delete
        #[arg(short, long, value_name = "FILE or URL")]
        file: Option<String>,
        /// The directory to delete
        #[arg(short, long, value_name = "DIR")]
        directory: Option<PathBuf>,
        /// Walk through sub directories
        #[arg(short, long, action = clap::ArgAction::SetTrue)]
        recursive: Option<bool>
    },
    /// Update otoroshi resources through json merge or json patch
    Patch {
        /// The resource name to operate on
        resource: String,
        /// The resource id to operate on
        id: String,
        /// The json object to merge
        merge: Option<String>,
        /// The file cxontaining the json object to merge
        #[arg(short, long, value_name = "FILE or URL")]
        file: Option<String>,
    },
    /// Update otoroshi resources
    Edit {
        /// The resource name to operate on
        resource: String,
        /// The resource id to operate on
        id: String,
        /// The file to sync
        #[arg(short, long, value_name = "FILE or URL")]
        file: Option<String>,
    },
    /// Create otoroshi resources
    Create {
        /// The resource name to operate on
        resource: String,
        /// The file to sync
        #[arg(short, long, value_name = "FILE or URL")]
        file: Option<String>,
    },
    /// Synchronise otoroshi resources from files or directories
    Apply {
        /// The file to sync
        #[arg(short, long, value_name = "FILE or URL")]
        file: Option<String>,
        /// The directory to sync
        #[arg(short, long, value_name = "DIR")]
        directory: Option<PathBuf>,
        /// Walk through sub directories
        #[arg(short, long, action = clap::ArgAction::SetTrue)]
        recursive: Option<bool>,
        /// Keep watching file changes
        #[arg(short, long, action = clap::ArgAction::SetTrue)]
        watch: Option<bool>,
    },
    /// Export otoroshi resources to files or directories
    Export {
        #[arg(short, long, value_name = "FILE")] // ok just file because writing
        file: Option<PathBuf>,
        /// The directory to sync
        #[arg(short, long, value_name = "DIR")]
        directory: Option<PathBuf>,
        /// Split the export into one entity per file
        #[arg(long, action = clap::ArgAction::SetTrue)]
        split_files: Option<bool>,
        /// Split the export into one entity per file
        #[arg(long, action = clap::ArgAction::SetTrue)]
        kube: Option<bool>,
        /// Export in ndjson format
        #[arg(long, action = clap::ArgAction::SetTrue)]
        nd_json: Option<bool>,
    },
    /// Import data from an export file
    Import {
        /// The file to import
        #[arg(short, long, value_name = "FILE or URL")]
        file: String,
        /// import from ndjson format
        #[arg(long, action = clap::ArgAction::SetTrue)]
        nd_json: Option<bool>,
    }
}

#[derive(Subcommand, Clone, Debug)]
pub enum SidecarSubCommand {
    /// Display instructions to install/run the sidecar
    Howto {},
    /// Run otoroshi sidecar
    Run {
       /// The sidecar config file
       #[arg(short, long, value_name = "FILE or URL")]
       file: Option<String>,
       // /// The sidecar dns server port
       // #[arg(long, value_name = "PORT")]
       // dns_port: Option<u16>,
    }, 
    GenerateConfig {
        /// The sidecar config file to generate
        #[arg(short, long, value_name = "FILE")]
        file: Option<String>,
     }, 
    /// Install transparent proxing of the mesh calls through iptables rules 
    Install {
        /// Dry run, do not apply the changes
        #[arg(long, action = clap::ArgAction::SetTrue)]
        dry_run: Option<bool>,
        /// The sidecar config file
        #[arg(short, long, value_name = "FILE or URL")]
        file: Option<String>,
        /// The user that will run the sidecar process using the dedicated user or runuser 
        #[arg(short, long, value_name = "USER")]
        user: String,
    },
    /// Uninstall transparent proxing of the mesh calls through iptables rules 
    Uninstall {
        /// Dry run, do not apply the changes
        #[arg(long, action = clap::ArgAction::SetTrue)]
        dry_run: Option<bool>,
    }
}

#[derive(Subcommand, Clone, Debug)]
pub enum CloudApimSubCommand {
    /// Login to your cloud-apim account
    Login,
    /// List your deployments
    List,
    /// Logout from your cloud-apim account
    Logout,
    /// Add the cluster to the possible otoroshictl configs
    Link {
        /// Name of the deployment
        name: String,
    },
    /// Add the cluster to the possible otoroshictl configs and set it as the current one
    Use {
        /// Name of the deployment
        name: String,
    },
    /// Restart this otoroshi cluster on cloud-apim
    Restart {
        /// Name of the deployment
        name: String,
    },
}

#[derive(Subcommand, Clone, Debug)]
pub enum Commands {
    /// Manage all the resources (entities) of the current otoroshi cluster
    Resources {
        #[command(subcommand)]
        command: ResourcesSubCommand
    },
    /// Manage an otoroshi mesh sidecar
    Sidecar {
        #[command(subcommand)]
        command: SidecarSubCommand,
    },
    /// Manage otoroshi tcp tunnel to access tcp resources through the current otoroshi cluster (not implemented yet)
    TcpTunnel {},
    /// Manage cloud apim clusters
    CloudApim {
        #[command(subcommand)]
        command: CloudApimSubCommand,
    },
    /// Exposes local processes on the current otoroshi cluster through the otoroshi remote tunnel feature
    RemoteTunnel {
        /// the local host forwarded to
        #[clap(long, default_value = "localhost")]
        local_host: String,
        /// the local port forwarded to
        #[clap(long, default_value = "8080")]
        local_port: i32,
        /// local process exposed as tls ?
        #[clap(long, action, default_value = "false")]
        local_tls: bool,
        /// enable expose mode
        #[clap(long, action, default_value = "false")]
        expose: bool,
        /// the exposed domain
        #[clap(long)]
        remote_domain: Option<String>,
        /// the exposed subdomain
        #[clap(long)]
        remote_subdomain: Option<String>,
        /// enable tls want mode
        #[clap(long, action, default_value = "false")]
        tls: bool,
        /// the tunnel id
        #[clap(long, default_value = "cli")]
        tunnel: String,
    },
    /// Display the version of the current otoroshi cluster
    Version {
    },
    /// Display the informations about the current otoroshi cluster
    Infos {
    },
    /// Display the managed entities of the current otoroshi cluster
    Entities {
    },
    /// Display the health status of the current otoroshi cluster
    Health {
    },
    /// Display metrics of the current otoroshi cluster
    Metrics {
        /// Optional comma separated list of columns to display
        #[arg(long)]
        columns: Vec<String>,
        /// Optional comma separated filters for metrics name
        #[arg(long)]
        filters: Option<String>,
    },
    /// Manage all the otoroshi cluster configurations you want to connect to with otoroshictl
    Config {
        #[command(subcommand)]
        command: ConfigSubCommand,
    },
}

impl CliOpts {
    pub fn build_from_command_line() -> CliOpts {
        CliOpts::parse()
    }
}