Skip to content

Commit

Permalink
Interactive mode (#165)
Browse files Browse the repository at this point in the history
The changes introduced by this PR as follows:
* Adds `ie interactive` implementation to support executing markdown
documents interactively.
* Adds `ie inspect` to view the parsed data of a markdown document
produced by Innovation engines parser. This feature is still
experimental and only shows some of the parsing output for now.
* Updates the markdown parser to associate the last paragraph of
markdown document as the description for a codeblock.
* Adds a completely new rendering system using `bubble-tea` to support
interactive mode. Both `ie execute` and `ie test` will be rewritten in
the future to utilize a similar rendering implementation.
* Changes the status update for integration with the azure extensionto
nest codeblocks underneath steps and for codeblocks to include their
descriptions for learn mode.
  • Loading branch information
vmarcella authored Feb 16, 2024
1 parent 48edb14 commit 3b01042
Show file tree
Hide file tree
Showing 25 changed files with 1,210 additions and 345 deletions.
62 changes: 2 additions & 60 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: build-ie build-api build-all run-ie run-api clean test-all test all
.PHONY: build-ie build-all run-ie clean test-all test all

BINARY_DIR := bin
IE_BINARY := $(BINARY_DIR)/ie
Expand All @@ -10,15 +10,8 @@ build-ie:
@echo "Building the Innovation Engine CLI..."
@CGO_ENABLED=0 go build -o "$(IE_BINARY)" cmd/ie/ie.go

build-api:
@echo "Building the Innovation Engine API..."
@CGO_ENABLED=0 go build -o "$(API_BINARY)" cmd/api/main.go

build-runner: build-ie build-api
@echo "Building the Innovation Engine Runner..."
@CGO_ENABLED=0 go build -o "$(BINARY_DIR)/runner" cmd/runner/main.go

build-all: build-ie build-api build-runner
build-all: build-ie

# ------------------------------ Install targets -------------------------------

Expand Down Expand Up @@ -67,58 +60,7 @@ run-ie: build-ie
@echo "Running the Innovation Engine CLI"
@"$(IE_BINARY)"

run-api: build-api
@echo "Running the Innovation Engine API"
@"$(API_BINARY)"

clean:
@echo "Cleaning up"
@rm -rf "$(BINARY_DIR)"

# ----------------------------- Docker targets ---------------------------------

API_IMAGE_TAG ?= latest

# Builds the API container.
build-api-container:
@echo "Building the Innovation Engine API container"
@docker build -t innovation-engine-api:$(API_IMAGE_TAG) -f infra/api/Dockerfile .


# ----------------------------- Kubernetes targets -----------------------------

# Applies the ingress controller to the cluster and waits for it to be ready.
k8s-deploy-ingress-controller:
@echo "Deploying the ingress controller to your local cluster..."
@kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.1/deploy/static/provider/cloud/deploy.yaml
@kubectl wait --namespace ingress-nginx --for=condition=ready pod --selector=app.kubernetes.io/component=controller --timeout=120s

# Deploys the API deployment, service, and ingress specifications to the
# cluster, allowing the API to be accessed via the ingress controller.
k8s-deploy-api: build-api-container
@echo "Deploying the Innovation Engine API container to your local cluster..."
@kubectl apply -f infra/api/deployment.yaml
@kubectl apply -f infra/api/service.yaml
@kubectl apply -f infra/api/ingress.yaml

k8s-initialize-cluster: k8s-deploy-ingress-controller k8s-deploy-api
@echo "Set up Kubernetes cluster for local development."

k8s-delete-ingress-controller:
@echo "Deleting the ingress controller from your local cluster..."
@kubectl delete -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.1/deploy/static/provider/cloud/deploy.yaml

k8s-delete-api:
@echo "Deleting the Innovation Engine API container from your local cluster..."
@kubectl delete -f infra/api/deployment.yaml
@kubectl delete -f infra/api/service.yaml
@kubectl delete -f infra/api/ingress.yaml

k8s-refresh-api: k8s-delete-api k8s-deploy-api
@echo "Refreshed the Innovation Engine API container in your local cluster..."

k8s-delete-cluster: k8s-delete-api k8s-delete-ingress-controller
@echo "Deleted Kubernetes cluster for local development."

k8s-refresh-cluster: k8s-delete-cluster k8s-initialize-cluster
@echo "Refreshed Kubernetes cluster for local development."
55 changes: 0 additions & 55 deletions cmd/api/main.go

This file was deleted.

11 changes: 0 additions & 11 deletions cmd/api/types.go

This file was deleted.

109 changes: 109 additions & 0 deletions cmd/ie/commands/inspect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package commands

import (
"fmt"
"os"
"strings"

"github.com/Azure/InnovationEngine/internal/engine"
"github.com/Azure/InnovationEngine/internal/logging"
"github.com/Azure/InnovationEngine/internal/ui"
"github.com/spf13/cobra"
)

// Register the command with our command runner.
func init() {
rootCommand.AddCommand(inspectCommand)

// String flags
inspectCommand.PersistentFlags().
String("correlation-id", "", "Adds a correlation ID to the user agent used by a scenarios azure-cli commands.")
inspectCommand.PersistentFlags().
String("subscription", "", "Sets the subscription ID used by a scenarios azure-cli commands. Will rely on the default subscription if not set.")
inspectCommand.PersistentFlags().
String("working-directory", ".", "Sets the working directory for innovation engine to operate out of. Restores the current working directory when finished.")

// StringArray flags
inspectCommand.PersistentFlags().
StringArray("var", []string{}, "Sets an environment variable for the scenario. Format: --var <key>=<value>")
}

var inspectCommand = &cobra.Command{
Use: "inspect",
Short: "Execute a document in inspect mode.",
Run: func(cmd *cobra.Command, args []string) {
markdownFile := args[0]
if markdownFile == "" {
logging.GlobalLogger.Errorf("Error: No markdown file specified.")
cmd.Help()
os.Exit(1)
}

environmentVariables, _ := cmd.Flags().GetStringArray("var")
// features, _ := cmd.Flags().GetStringArray("feature")

// Parse the environment variables from the command line into a map
cliEnvironmentVariables := make(map[string]string)
for _, environmentVariable := range environmentVariables {
keyValuePair := strings.SplitN(environmentVariable, "=", 2)
if len(keyValuePair) != 2 {
logging.GlobalLogger.Errorf(
"Error: Invalid environment variable format: %s",
environmentVariable,
)
fmt.Printf("Error: Invalid environment variable format: %s", environmentVariable)
cmd.Help()
os.Exit(1)
}

cliEnvironmentVariables[keyValuePair[0]] = keyValuePair[1]
}
// Parse the markdown file and create a scenario
scenario, err := engine.CreateScenarioFromMarkdown(
markdownFile,
[]string{"bash", "azurecli", "azurecli-inspect", "terraform"},
cliEnvironmentVariables,
)
if err != nil {
logging.GlobalLogger.Errorf("Error creating scenario: %s", err)
fmt.Printf("Error creating scenario: %s", err)
os.Exit(1)
}

if err != nil {
logging.GlobalLogger.Errorf("Error creating engine: %s", err)
fmt.Printf("Error creating engine: %s", err)
os.Exit(1)
}

fmt.Println(ui.ScenarioTitleStyle.Render(scenario.Name))
for stepNumber, step := range scenario.Steps {
stepTitle := fmt.Sprintf(" %d. %s\n", stepNumber+1, step.Name)
fmt.Println(ui.StepTitleStyle.Render(stepTitle))
for codeBlockNumber, codeBlock := range step.CodeBlocks {
fmt.Println(
ui.InteractiveModeCodeBlockDescriptionStyle.Render(
fmt.Sprintf(
" %d.%d %s",
stepNumber+1,
codeBlockNumber+1,
codeBlock.Description,
),
),
)
fmt.Print(
ui.IndentMultiLineCommand(
fmt.Sprintf(
" %s",
ui.InteractiveModeCodeBlockStyle.Render(
codeBlock.Content,
),
),
6),
)
fmt.Println()
}
}

},
}
100 changes: 96 additions & 4 deletions cmd/ie/commands/interactive.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,107 @@
package commands

import (
"fmt"
"os"
"strings"

"github.com/Azure/InnovationEngine/internal/engine"
"github.com/Azure/InnovationEngine/internal/logging"
"github.com/spf13/cobra"
)

// Register the command with our command runner.
func init() {
rootCommand.AddCommand(interactiveCommand)

// String flags
interactiveCommand.PersistentFlags().
String("correlation-id", "", "Adds a correlation ID to the user agent used by a scenarios azure-cli commands.")
interactiveCommand.PersistentFlags().
String("subscription", "", "Sets the subscription ID used by a scenarios azure-cli commands. Will rely on the default subscription if not set.")
interactiveCommand.PersistentFlags().
String("working-directory", ".", "Sets the working directory for innovation engine to operate out of. Restores the current working directory when finished.")

// StringArray flags
interactiveCommand.PersistentFlags().
StringArray("var", []string{}, "Sets an environment variable for the scenario. Format: --var <key>=<value>")
}

var interactiveCommand = &cobra.Command{
Use: "interactive",
Short: "Execute a document in interactive mode.",
}
Run: func(cmd *cobra.Command, args []string) {
markdownFile := args[0]
if markdownFile == "" {
logging.GlobalLogger.Errorf("Error: No markdown file specified.")
cmd.Help()
os.Exit(1)
}

// / Register the command with our command runner.
func init() {
rootCommand.AddCommand(interactiveCommand)
verbose, _ := cmd.Flags().GetBool("verbose")
doNotDelete, _ := cmd.Flags().GetBool("do-not-delete")

subscription, _ := cmd.Flags().GetString("subscription")
correlationId, _ := cmd.Flags().GetString("correlation-id")
environment, _ := cmd.Flags().GetString("environment")
workingDirectory, _ := cmd.Flags().GetString("working-directory")

environmentVariables, _ := cmd.Flags().GetStringArray("var")
// features, _ := cmd.Flags().GetStringArray("feature")

// Known features
renderValues := false

// Parse the environment variables from the command line into a map
cliEnvironmentVariables := make(map[string]string)
for _, environmentVariable := range environmentVariables {
keyValuePair := strings.SplitN(environmentVariable, "=", 2)
if len(keyValuePair) != 2 {
logging.GlobalLogger.Errorf(
"Error: Invalid environment variable format: %s",
environmentVariable,
)
fmt.Printf("Error: Invalid environment variable format: %s", environmentVariable)
cmd.Help()
os.Exit(1)
}

cliEnvironmentVariables[keyValuePair[0]] = keyValuePair[1]
}
// Parse the markdown file and create a scenario
scenario, err := engine.CreateScenarioFromMarkdown(
markdownFile,
[]string{"bash", "azurecli", "azurecli-interactive", "terraform"},
cliEnvironmentVariables,
)
if err != nil {
logging.GlobalLogger.Errorf("Error creating scenario: %s", err)
fmt.Printf("Error creating scenario: %s", err)
os.Exit(1)
}

innovationEngine, err := engine.NewEngine(engine.EngineConfiguration{
Verbose: verbose,
DoNotDelete: doNotDelete,
Subscription: subscription,
CorrelationId: correlationId,
Environment: environment,
WorkingDirectory: workingDirectory,
RenderValues: renderValues,
})

if err != nil {
logging.GlobalLogger.Errorf("Error creating engine: %s", err)
fmt.Printf("Error creating engine: %s", err)
os.Exit(1)
}

// Execute the scenario
err = innovationEngine.InteractWithScenario(scenario)
if err != nil {
logging.GlobalLogger.Errorf("Error executing scenario: %s", err)
fmt.Printf("Error executing scenario: %s", err)
os.Exit(1)
}
},
}
13 changes: 0 additions & 13 deletions cmd/runner/main.go

This file was deleted.

Loading

0 comments on commit 3b01042

Please sign in to comment.