Skip to content
This repository has been archived by the owner on Dec 5, 2023. It is now read-only.

[tem-1559] instance deploy #30

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ instances can be started that share a port number.

Each instance runs as a Docker container.

## `tembo instance stop`

The `instance stop` command allows users to stop their running instances. It requires the name as a parameter.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this local only?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, the only command that hits the cloud is the instance deploy command at this point


Each instance runs as a Docker container. This command stops the container.

## `tembo instance deploy`

Uses information obtained with `auth login`. Provisions an instance in the cloud, via API, matching the config of a local instance
created using `instance create`.

## `tembo auth login`

The `auth login` command allows users to authenticate as a service user and obtain an API token that can be used on future authenticated requests.
Expand Down
78 changes: 77 additions & 1 deletion src/cli/instance.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Objects representing a user created local instance of a stack
// (a local container that runs with certain attributes and properties)

use crate::cli::config::Config;
use crate::cli::database::Database;
use crate::cli::docker::DockerError;
Expand All @@ -9,8 +8,12 @@ use crate::cli::stacks;
use crate::cli::stacks::{Stack, TrunkInstall};
use chrono::prelude::*;
use clap::ArgMatches;
use hyper::header;
use reqwest::header::HeaderMap;
use reqwest::StatusCode;
use serde::Deserialize;
use serde::Serialize;
use serde_json::json;
use simplelog::*;
use spinners::{Spinner, Spinners};
use std::cmp::PartialEq;
Expand Down Expand Up @@ -241,4 +244,77 @@ impl Instance {
name: name.to_string(),
})
}

pub fn deploy(&self, args: &ArgMatches) -> Result<String, Box<dyn Error>> {
let name = args
.try_get_one::<String>("name")
.clone()
.unwrap()
.unwrap()
.as_str();
info!("finding config for instance {name}");

let config = Config::new(args, &Config::full_path(args));
if config.cloud_account.is_none() || config.jwt.is_none() {
return Err(Box::new(DockerError::new(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this only run in Docker? If not that Docker error could maybe be confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a good point, this isn't running in Docker (rather locally) and could use its own error object, maybe ConfigError? Thoughts?

format!(
"You need to run {} before deploying instances",
"`auth login`"
)
.as_str(),
)));
}

let binding = config.cloud_account.unwrap();
let org_id = binding.organizations.first().unwrap().replace('\"', "");
let jwt = config.jwt.unwrap();

let client = reqwest::blocking::Client::new();
let request_url = format!("https://api.tembo.io/api/v1/orgs/{org_id}/instances");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this be configurable in the future? I could definitely see the need to run our cli against our lower environments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a good thought, I don't know, the current thinking was to go through the API for everything, what did you have in mind?


let headers: HeaderMap = {
vec![(
header::AUTHORIZATION,
format!("Bearer {}", jwt).parse().unwrap(),
)]
.into_iter()
.collect()
};

let payload = json!({
"instance_name": name,
"stack_type": "Standard",
"cpu": "1",
"environment": "dev",
"memory": "1Gi",
"storage": "10Gi",
"replicas": 1,
//"extensions": [],
//"trunk_installs": [],
//"app_services": {},
//"connection_pooler": {},
//"extra_domains_rw": [],
//"ip_allow_list": [],
//"postgres_configs": [],
});

let res = client
.post(request_url)
.headers(headers)
.json(&payload)
.send()?;

match res.status() {
StatusCode::ACCEPTED => {
info!("provisioning: https://cloud.tembo.io/orgs/{org_id}/clusters");

Ok(String::from("accepted"))
}
status_code if status_code.is_client_error() => {
info!("{}", status_code);
Err(From::from(format!("Client error: {status_code}")))
}
_ => Err(From::from("Client error")),
}
}
}
2 changes: 2 additions & 0 deletions src/cmd/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use simplelog::*;
use std::error::Error;

pub mod create;
pub mod deploy;
pub mod list;
pub mod start;
pub mod stop;
Expand All @@ -15,6 +16,7 @@ pub fn execute(args: &ArgMatches) -> Result<(), Box<dyn Error>> {
Some(("list", sub_matches)) => list::execute(sub_matches),
Some(("start", sub_matches)) => start::execute(sub_matches),
Some(("stop", sub_matches)) => stop::execute(sub_matches),
Some(("deploy", sub_matches)) => deploy::execute(sub_matches),
_ => unreachable!(),
};

Expand Down
42 changes: 42 additions & 0 deletions src/cmd/instance/deploy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// instance deploy command
use crate::cli::config::Config;
use anyhow::anyhow;
use clap::{Arg, ArgAction, ArgMatches, Command};
use simplelog::*;
use std::error::Error;

// example usage: tembo instance deploy -n my_local_instance
pub fn make_subcommand() -> Command {
Command::new("deploy")
.about("Command used to deploy a local instance to the Tembo cloud")
.arg(
Arg::new("name")
.short('n')
.long("name")
.action(ArgAction::Set)
.required(true)
.help("The name you want to use for this instance"),
)
}

pub fn execute(args: &ArgMatches) -> Result<(), Box<dyn Error>> {
let config = Config::new(args, &Config::full_path(args));
let name = args
.get_one::<String>("name")
.ok_or_else(|| anyhow!("Name is missing."))?;

if config.instances.is_empty() {
info!("No instances have been configured");
} else {
for instance in &config.instances {
if instance.name.clone().unwrap().to_lowercase() == name.to_lowercase() {
match instance.deploy(args) {
Ok(_) => info!(" instance deployed"),
Err(e) => warn!(" there was an error deploying the instance: {}", e),
}
}
}
}

Ok(())
}
2 changes: 1 addition & 1 deletion src/cmd/instance/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub fn execute(args: &ArgMatches) -> Result<(), Box<dyn Error>> {
}

info!("Start an instance using `tembo instance start -n <name>`");
info!("Coming soon: deploy an instance using `tembo instance deploy -n <name>`");
info!("Deploy an instance using `tembo instance deploy -n <name>`");
}

Ok(())
Expand Down
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ fn create_clap_command() -> Command {
.subcommand(cmd::instance::create::make_subcommand())
.subcommand(cmd::instance::list::make_subcommand())
.subcommand(cmd::instance::start::make_subcommand())
.subcommand(cmd::instance::stop::make_subcommand()),
.subcommand(cmd::instance::stop::make_subcommand())
.subcommand(cmd::instance::deploy::make_subcommand()),
)
.subcommand(
Command::new("auth")
Expand Down
Loading