Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add one-off command #65

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
193 changes: 193 additions & 0 deletions src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,199 @@ command!(
}
);

command!(
OneOff,
Copy link
Author

Choose a reason for hiding this comment

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

OneOff should be probably Run to mimic the kubectl command.

"one-off",
"exec specified command in a new container of the active pod",
|clap: App<'static, 'static>| clap.arg(
Arg::with_name("command")
.help("The command to execute")
.required(true)
.index(1)
).arg(
Arg::with_name("container")
.short("c")
.long("container")
.help("Exec in the image of the specified container")
.takes_value(true)
)
.arg(
Arg::with_name("terminal")
.short("t")
.long("terminal")
.help(
"Run the command in a new terminal. With --terminal ARG, ARG is used as the \
terminal command, otherwise the default is used ('set terminal <value>' to \
specify default)"
)
.takes_value(true)
.min_values(0)
),
vec!["one-off"],
|args: Vec<&str>, env: &Env| if args.len() <= 1 {
let mut v = Vec::new();
let argstart = args.get(0);
match env.current_object {
::KObj::Pod {
name: _,
ref containers,
} => for cont in containers.iter() {
if argstart.is_none() || cont.starts_with(argstart.unwrap()) {
v.push(cont.clone());
}
},
_ => {}
}
(
match argstart {
Some(line) => line.len(),
None => 0,
},
v,
)
} else {
(0, Vec::new())
},
|matches, env, writer| {
let cont = match matches.value_of("container") {
Some(c) => match env.current_object {
::KObj::Pod {
name: _,
ref containers,
} => {
if !containers.contains(&c.to_string()) {
clickwrite!(
writer,
"Specified container not found. Please specify one of: \
{:?}\n",
containers
);
return;
}
c
}
_ => {
clickwrite!(writer, "Need an active pod to run one-off command.\n");
return;
}
},
None => match env.current_object {
::KObj::Pod {
name: _,
ref containers,
} => {
if containers.len() == 1 {
containers[0].as_str()
} else {
clickwrite!(
writer,
"Pod has multiple containers, please specify one of: \
{:?}\n",
containers
);
return;
}
}
_ => {
clickwrite!(writer, "Need an active pod to run one-off command.\n");
return;
}
},
};

let cmd = matches.value_of("command").unwrap(); // safe as required
if let (Some(ref kluster), Some(ref ns), Some(ref pod)) = (
env.kluster.as_ref(),
env.current_object_namespace.as_ref(),
env.current_pod(),
) {
let url = format!("/api/v1/namespaces/{}/pods/{}", ns, pod);
let pod_opt: Option<Pod> = env.run_on_kluster(|k| k.get(url.as_str()));
let container_image = if let Some(pod) = pod_opt {
let mut image = "";
if let Some(ref container_statuses) = pod.status.container_statuses {
for container_status in container_statuses.iter() {
if cont == container_status.name {
image = &container_status.image;
}
}
if image == "" {
clickwrite!(writer, "Couldon't get container image\n");
return;
} else {
String::from(image)
}
} else {
clickwrite!(writer, "Couldon't get container image\n");
return;
}
} else {
clickwrite!(writer, "Couldon't get container image\n");
return;
};

if matches.is_present("terminal") {
let terminal = if let Some(t) = matches.value_of("terminal") {
t
} else if let Some(ref t) = env.click_config.terminal {
t
} else {
"xterm -e"
};
let mut targs: Vec<&str> = terminal.split_whitespace().collect();
let mut kubectl_args = vec![
"kubectl",
"--namespace",
ns,
"--context",
&kluster.name,
"run",
"one-off-shell",
"--restart=Never",
"-i",
"--rm",
"--tty",
"--image",
container_image.as_str(),
];
targs.append(&mut kubectl_args);
targs.push(cmd);
clickwrite!(writer, "Starting in terminal\n");
if let Err(e) = duct::cmd(targs[0], &targs[1..]).start() {
clickwrite!(
writer,
"Could not launch in terminal: {}\n",
e.description()
);
}
} else {
clickwrite!(writer, "Starting one-off container with image {:?}\n", container_image);
let status = Command::new("kubectl")
.arg("--namespace")
.arg(ns)
.arg("--context")
.arg(&kluster.name)
.arg("run")
.arg("one-off-shell")
Copy link
Author

Choose a reason for hiding this comment

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

Could be named more specific eg. <pod_name>-one-off

.arg("--restart=Never")
.arg("-i")
.arg("--rm")
.arg("--tty")
.arg("--image")
.arg(container_image.as_str())
.arg(cmd)
.status()
.expect("failed to execute kubectl");
if !status.success() {
clickwrite!(writer, "kubectl exited abnormally\n");
}
}
} else {
clickwrite!(writer, "Need an active pod in order to run one-off command.\n");
}
}
);

command!(
Describe,
"describe",
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ fn main() {
commands.push(Box::new(cmd::Secrets::new()));
commands.push(Box::new(cmd::PortForward::new()));
commands.push(Box::new(cmd::PortForwards::new()));
commands.push(Box::new(cmd::OneOff::new()));

let mut rl = Editor::<ClickCompleter>::new();
rl.load_history(hist_path.as_path()).unwrap_or_default();
Expand Down