# Tembo CLI

Tembo CLI allows users to experience [Tembo](https://tembo.io) locally, as well as,
manage and deploy to Tembo Cloud. It abstracts away complexities of configuring,
managing, and running Postgres.

## Getting Started

### Installing CLI

Using homebrew

```
brew tap tembo-io/tembo
brew install tembo-cli
```

Using cargo

```
cargo install tembo-cli
```

### Commands

#### `tembo init`

The `tembo init` command initializes your environment with following files. Run init in the directory you want to create the `tembo.toml` file.

* `tembo.toml` example configuration file
* `~/.tembo/context` file with various contexts user can connect to
* `~/.tembo/credentials` file with credentials & api urls

For more information: `tembo init --help`

#### Add Tembo Cloud info

To provision instances on Tembo Cloud using CLI you will need to configure `org_id` & `tembo_access_token`

* fetch the `org_id` from Tembo Cloud and add it as `org_id` in context file generated above
* generate a JWT token using steps [here](https://tembo.io/docs/tembo-cloud/security-and-authentication/api-authentication/) & add it as `tembo_access_token` to the credentials file generated above.

#### `tembo context list/set`

tembo context works like [kubectl context](https://www.notion.so/abee0b15119343e4947692feb740e892?pvs=21). User can set context for local docker environment or tembo cloud (dev/qa/prod) with org_id. When they run any of the other commands it will run in the context selected. Default context will be local.

#### `tembo validate`

Validates `tembo.toml` and other configurations files.

#### `tembo apply`

Validates tembo.toml (same as `tembo validate`) and applies the changes to the context selected.

##### Environment:

  * ###### Local Docker:
    * runs `docker-compose down` to bring down all existing containers
    * generates `Dockerfile` for each instance & builds a docker image
    * generates `docker-compose` to provision all instances
    * runs `docker-compose up -d` to spin up all instances

  * ###### Tembo-Cloud: 
    * Creates/updates instance on tembo-cloud by calling the api against the appropriate environment

##### Flags: 
  * `--merge`: Overlays Tembo.toml by another toml file for a specific context
  *  `--set` : Specifies a single instance setting by assigning a new value

#### `tembo logs`

Retrieves log data from the specified Tembo instances. Depending on your current context, it will fetch logs from either local Docker containers or Tembo Cloud instances.


#### `tembo delete`

- **local docker:** runs `docker-compose down` command to bring down all containers
- **tembo-cloud:** deletes the instance on tembo-cloud by calling the api

## Developing Tembo CLI

### Local Testing

Clone this repo and run:

`cargo install --path .`

If the install path is in your shell path, you can then run `tembo help` and other `tembo` commands.

You can run this command to use the local code for any tembo command during development:

```
alias tembo='cargo run --'
```

### Generating Rust Client from API

[OpenAPI Generator](https://openapi-generator.tech/) tool is used to generate Rust Client.

Install OpenAPI Generator if not already by following steps [here](https://openapi-generator.tech/docs/installation)

#### Data plane API client

Go to `tembodataclient` directory in your terminal.

Delete the contents of the directory first and then run following command to re-generate the rust client code for the API.

```bash
openapi-generator generate -i https://api.data-1.use1.tembo.io/api-docs/openapi.json  -g rust -o . --additional-properties=packageName=tembodataclient
```

* Go to `tembodataclient/src/lib.rs` & add following line at the top to disable clippy for the generated code

```
#![allow(clippy::all)]
```

#### Control plane API client

Go to `temboclient` directory in your terminal.

Delete the contents of the directory first and then run following command to re-generate the rust client code for the API.

```bash
openapi-generator generate -i https://api.tembo.io/api-docs/openapi.json  -g rust -o . --additional-properties=packageName=temboclient
```

* Go to `temboclient/src/lib.rs` & add following line at the top to disable clippy for the generated code

```
#![allow(clippy::all)]
```

* Create `/temboclient/src/models/impls.rs` file & add following code to it:

```
use std::str::FromStr;

use super::{Cpu, Environment, Memory, StackType, Storage};

impl FromStr for Cpu {
    type Err = ();

    fn from_str(input: &str) -> core::result::Result<Cpu, Self::Err> {
        match input {
            "0.25" => Ok(Cpu::Variant0Period25),
            "0.5" => Ok(Cpu::Variant0Period5),
            "1" => Ok(Cpu::Variant1),
            "2" => Ok(Cpu::Variant2),
            "4" => Ok(Cpu::Variant4),
            "8" => Ok(Cpu::Variant8),
            "16" => Ok(Cpu::Variant16),
            "32" => Ok(Cpu::Variant32),
            _ => Err(()),
        }
    }
}

impl FromStr for Memory {
    type Err = ();

    fn from_str(input: &str) -> core::result::Result<Memory, Self::Err> {
        match input {
            "1Gi" => Ok(Memory::Variant1Gi),
            "2Gi" => Ok(Memory::Variant2Gi),
            "4Gi" => Ok(Memory::Variant4Gi),
            "8Gi" => Ok(Memory::Variant8Gi),
            "16Gi" => Ok(Memory::Variant16Gi),
            "32Gi" => Ok(Memory::Variant32Gi),
            _ => Err(()),
        }
    }
}

impl FromStr for Environment {
    type Err = ();

    fn from_str(input: &str) -> core::result::Result<Environment, Self::Err> {
        match input {
            "dev" => Ok(Environment::Dev),
            "test" => Ok(Environment::Test),
            "prod" => Ok(Environment::Prod),
            _ => Err(()),
        }
    }
}

impl FromStr for Storage {
    type Err = ();

    fn from_str(input: &str) -> core::result::Result<Storage, Self::Err> {
        match input {
            "10Gi" => Ok(Storage::Variant10Gi),
            "50Gi" => Ok(Storage::Variant50Gi),
            "100Gi" => Ok(Storage::Variant100Gi),
            "200Gi" => Ok(Storage::Variant200Gi),
            "300Gi" => Ok(Storage::Variant300Gi),
            "400Gi" => Ok(Storage::Variant400Gi),
            "500Gi" => Ok(Self::Variant500Gi),
            _ => Err(()),
        }
    }
}

impl FromStr for StackType {
    type Err = ();

    fn from_str(input: &str) -> core::result::Result<StackType, Self::Err> {
        match input {
            "Standard" => Ok(StackType::Standard),
            "MessageQueue" => Ok(StackType::MessageQueue),
            "MachineLearning" => Ok(StackType::MachineLearning),
            "OLAP" => Ok(StackType::Olap),
            "VectorDB" => Ok(StackType::VectorDb),
            "OLTP" => Ok(StackType::Oltp),
            "DataWarehouse" => Ok(StackType::DataWarehouse),
            "Geospatial" => Ok(StackType::Geospatial),
            _ => Err(()),
        }
    }
}
```

* Add following line towards the end of `/temboclient/src/models/mod.rs`

```
pub mod impls;
```

## Contributing

Before you start working on something, it's best to check if there is an existing plan
first. Join our [Slack community](https://join.slack.com/t/trunk-crew/shared_invite/zt-1yiafma92-hFHq2xAN0ukjg_2AsOVvfg) and ask there.

## Semver

Tembo CLI is following [Semantic Versioning 2.0](https://semver.org/).
