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

Move Cloud commands out of Spin CLI and into a plugin #1452

Merged
merged 13 commits into from
Jun 7, 2023
Merged
Show file tree
Hide file tree
Changes from 7 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
157 changes: 157 additions & 0 deletions docs/content/sips/013-cloud-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
title = "SIP 013 - "Cloud Plugin"
template = "main"
date = "2023-05-10T23:09:00Z"

---

Summary: This improvement proposal describes the plan to move the cloud-specific
functionality from the core Spin command-line interface to a separate plugin.

Owners: matt.fisher@fermyon.com

Created: May 10, 2023

## Background

The intended outcome of this SIP is to provide a set of recommendations to move
the core logic of `spin login` and `spin deploy` into a separate `spin cloud`
plugin, while continuing to provide a great out-of-the-box experience for those
wanting to deploy their applications to the Fermyon Cloud.

## Proposal

This document proposes to move two commands from the Spin command-line interface
to a separate plugin:

1. `spin login`
2. `spin deploy`

These commands will be moved to a separate `spin cloud` plugin. This document
will also recommend possible approaches to ensure these commands remain easily
accessible to new and existing users of the Spin command-line interface.

This enables the Spin team to release the core Spin runtime and the Spin
command-line interface as stable, while also enabling the functionality of the
Fermyon Cloud to change and iterate over time.

## Rationale

Several commands were introduced to the Spin CLI, making it very simple for
someone to deploy their application to the Fermyon Cloud:

- `spin login`
- `spin deploy`

These commands are orthogonal to the concerns of Spin's core runtime. These
commands involve the packaging and distribution of a Spin application to the
Fermyon Cloud. They are considered "adjacent" to the Spin user experience as
they do not assist the developer with writing their application, nor do they
relate to Spin's core runtime; they provide a simple experience to ship their
application to the Fermyon Cloud.

`spin login` and `spin deploy` communicate with the Fermyon Cloud API. As new
features are introduced to the Fermyon Cloud, the API may change and evolve over
time.

Building a simple, delightful on-ramp experience for the Fermyon Cloud remains
top priority. It is especially important that we continue to provide a very
simple on-ramp experience so users can readily deploy their Spin applications to
a production-grade system.

Spin’s existing plug-in system allows the Spin community to add and extend the
functionality of Spin without introducing changes to the Spin command-line
interface. Plug-ins are described in more detail in [SIP 006 - Spin
Plugins](./006-spin-plugins.md). This allows us to ship `spin login` and `spin
deploy` as separate, discrete functionality while ensuring users can still
access its functionality through familiar tooling.

## Specification

The proposal herein suggests that we re-release the core logic of `spin login`
and `spin deploy` under a separate `spin cloud` plugin. Spin will alias the
existing `spin login` and `spin deploy` commands to their respective `spin
cloud` counterparts to retain backwards compatibility.

When a user executes `spin login` or `spin deploy` and the cloud plugin is not
installed on their system, Spin will inform the user that the `spin cloud`
plugin has not been installed, then prompt the user to install the plugin.

```
kate-goldenring marked this conversation as resolved.
Show resolved Hide resolved
$ spin login
The `cloud` plugin is required. Installing now.
Are you sure you want to install plugin 'cloud' [...]? yes
Plugin 'cloud' was installed successfully!
```

kate-goldenring marked this conversation as resolved.
Show resolved Hide resolved
## Future design considerations

### `spin cloud config`

An early prototype of the `spin cloud` plugin proposed several changes to the
`spin cloud deploy` command, including a new `spin cloud config` command. The
proposed command would configure the "current" Spin app for deployment with an
interactive prompt. This command would be optional, and calling `spin cloud
deploy` on an application that was not yet configured would invoke `spin cloud
configure` in the background. The experience looked something like this:

```
$ spin cloud deploy
The current Spin app hasn't been set up for deployment yet.
Pick a new or existing deployment config:

> Fermyon Cloud - New Deployment
Fermyon Cloud - 'spicy-meatballs'

Creating new deployment...
New deployment created: 'mighty-hamster'

[...SNIP normal deploy flow...]
```

While the move to a separate `spin cloud` plugin does not reject this idea
outright, the goal of this SIP is to make the least invasive change to the
existing `spin login` and `spin deploy` experience. Future iterations to the
`spin login` and `spin deploy` experience can be addressed in future updates to
the plugin.

In fact, the movement to a separate `spin cloud` plugin grants us the
flexibility to make changes to the core `spin deploy` experience without forcing
us to wait until Spin 2.0. If anything, this proposal enables us to make these
changes to the `spin cloud` plugin without waiting for a new release of Spin.

### A generic `spin cloud` plugin supporting multiple cloud providers

kate-goldenring marked this conversation as resolved.
Show resolved Hide resolved
One recommendation was to design `spin cloud` in a generic fashion. In this
manner, "cloud providers" (including Fermyon Cloud) would integrate with `spin
cloud` . Customers would use `spin cloud login` and `spin cloud deploy` to
deploy Spin applications to their hosting platform of choice.

We hope to one day be in a position where partners are coming to us to design
such a system. We hope that might happen in the future (and we are open to the
idea of collaborating on such a project!). For the initial launch, the goal of
this SIP is to make the least invasive change to the existing `spin login` and
`spin deploy` experience.

### Will users be able to run CRUD operations on Cloud specific objects (like a KV store)?

The goal of this SIP is to find a new home for `spin login` and `spin deploy`.
Future iterations to the `spin cloud` plugin (such as a `spin cloud kv` command)
may be provided in future updates to the plugin.

## Open Questions

> How do we ensure the `spin cloud` plugin is kept up-to-date? Do we ask the
> user to run `spin plugin update`? Do we inform them that a new version of the
> plugin is available? Do we update the plugin for the user?

The goal of this SIP is to find a new home for `spin login` and `spin deploy`.
Future iterations to the plugin system can be handled separately as a feature
enhancement. For the time being, asking the user to run `spin plugin update`
aligns with the current plugin model.

> How do we install the plugin? Install on first invocation of `spin login` or
> `spin deploy`? Install the first time the user runs a regular `spin` command?

Per the current plugin system, we will return an error when the user attempts to
run a command when the plugin does not exist with a helpful message. Asking the
user to run `spin plugin install` aligns with the current plugin model.
13 changes: 5 additions & 8 deletions src/bin/spin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ use is_terminal::IsTerminal;
use lazy_static::lazy_static;
use spin_cli::commands::{
build::BuildCommand,
cloud::CloudCommands,
deploy::DeployCommand,
cloud::{CloudCommand, DeployCommand, LoginCommand},
external::execute_external_subcommand,
login::LoginCommand,
new::{AddCommand, NewCommand},
plugins::PluginCommands,
registry::RegistryCommands,
Expand Down Expand Up @@ -54,8 +52,7 @@ enum SpinApp {
New(NewCommand),
Add(AddCommand),
Up(UpCommand),
#[clap(subcommand)]
Cloud(CloudCommands),
Cloud(CloudCommand),
kate-goldenring marked this conversation as resolved.
Show resolved Hide resolved
// acts as a cross-level subcommand shortcut -> `spin cloud deploy`
Deploy(DeployCommand),
// acts as a cross-level subcommand shortcut -> `spin cloud login`
Expand Down Expand Up @@ -88,9 +85,9 @@ impl SpinApp {
Self::Up(cmd) => cmd.run().await,
Self::New(cmd) => cmd.run().await,
Self::Add(cmd) => cmd.run().await,
Self::Cloud(cmd) => cmd.run().await,
Self::Deploy(cmd) => cmd.run().await,
Self::Login(cmd) => cmd.run().await,
Self::Cloud(cmd) => cmd.run(SpinApp::command()).await,
Self::Deploy(cmd) => cmd.run(SpinApp::command()).await,
Self::Login(cmd) => cmd.run(SpinApp::command()).await,
Self::Registry(cmd) => cmd.run().await,
Self::Build(cmd) => cmd.run().await,
Self::Trigger(TriggerCommands::Http(cmd)) => cmd.run().await,
Expand Down
4 changes: 0 additions & 4 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@
pub mod build;
/// Commands for publishing applications to the Fermyon Platform.
pub mod cloud;
/// Command to package and upload an application to the Fermyon Platform.
pub mod deploy;
/// Commands for external subcommands (i.e. plugins)
pub mod external;
/// Command for logging into the Fermyon Platform.
pub mod login;
/// Command for creating a new application.
pub mod new;
/// Command for adding a plugin to Spin
Expand Down
71 changes: 55 additions & 16 deletions src/commands/cloud.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,63 @@
use crate::commands::external::execute_external_subcommand;
use anyhow::Result;
use clap::Subcommand;
use clap::Args;

use super::deploy::DeployCommand;
use super::login::LoginCommand;
#[derive(Debug, Args, PartialEq)]
#[clap(
about = "Package and upload an application to the Fermyon Platform",
kate-goldenring marked this conversation as resolved.
Show resolved Hide resolved
allow_hyphen_values = true,
disable_help_flag = true
)]
pub struct DeployCommand {
/// All args to be passed through to the plugin
#[clap(hide = true)]
args: Vec<String>,
}

#[derive(Debug, Args, PartialEq)]
#[clap(
about = "Login to the Fermyon Platform.",
kate-goldenring marked this conversation as resolved.
Show resolved Hide resolved
allow_hyphen_values = true,
disable_help_flag = true
)]
pub struct LoginCommand {
/// All args to be passed through to the plugin
#[clap(hide = true)]
args: Vec<String>,
}

#[derive(Debug, Args, PartialEq)]
#[clap(
about = "Commands for publishing applications to the Fermyon Platform.",
kate-goldenring marked this conversation as resolved.
Show resolved Hide resolved
allow_hyphen_values = true,
disable_help_flag = true
)]
pub struct CloudCommand {
/// All args to be passed through to the plugin
#[clap(hide = true)]
args: Vec<String>,
}

/// Commands for publishing applications to the Fermyon Platform.
#[derive(Subcommand, Debug)]
pub enum CloudCommands {
/// Package and upload an application to the Fermyon Platform.
Deploy(DeployCommand),
impl CloudCommand {
pub async fn run(self, app: clap::App<'_>) -> Result<()> {
let mut cmd = vec!["cloud".to_string()];
cmd.append(&mut self.args.clone());
execute_external_subcommand(cmd, app).await
}
}

/// Log into the Fermyon Platform.
Login(LoginCommand),
impl DeployCommand {
pub async fn run(self, app: clap::App<'_>) -> Result<()> {
let mut cmd = vec!["cloud".to_string(), "deploy".to_string()];
cmd.append(&mut self.args.clone());
execute_external_subcommand(cmd, app).await
}
}

impl CloudCommands {
pub async fn run(self) -> Result<()> {
match self {
Self::Deploy(cmd) => cmd.run().await,
Self::Login(cmd) => cmd.run().await,
}
impl LoginCommand {
pub async fn run(self, app: clap::App<'_>) -> Result<()> {
let mut cmd = vec!["cloud".to_string(), "login".to_string()];
cmd.append(&mut self.args.clone());
execute_external_subcommand(cmd, app).await
}
}
Loading