Giter Club home page Giter Club logo

floe's Introduction

Floe

CI Code Climate Test Coverage

Overview

Floe is a runner for Amazon States Language workflows with support for Docker resources and running on Docker, Podman, or Kubernetes.

Installation

Install the gem and add to the application's Gemfile by executing:

$ bundle add floe

If bundler is not being used to manage dependencies, install the gem by executing:

$ gem install floe

Usage

Floe can be run as a command-line utility or as a ruby class.

Command Line

bundle exec ruby exe/floe --workflow examples/workflow.asl --inputs='{"foo": 1}'

By default Floe will use docker to run docker:// type resources, but podman and kubernetes are also supported runners. A different runner can be specified with the --docker-runner option:

bundle exec ruby exe/floe --workflow examples/workflow.asl --inputs='{"foo": 1}' --docker-runner podman
bundle exec ruby exe/floe --workflow examples/workflow.asl --inputs='{"foo": 1}' --docker-runner kubernetes --docker-runner-options namespace=default server=https://k8s.example.com:6443 token=my-token

If your workflow has Credentials you can provide a payload that will help resolve those credentials references at runtime.

For example if your workflow had the following Credentials field with a JSON Path property:

"Credentials": {
  "RoleArn.$": "$.roleArn"
}

You can provide that at runtime via the --credentials parameter:

bundle exec ruby exe/floe --workflow my-workflow.asl --credentials='{"roleArn": "arn:aws:iam::111122223333:role/LambdaRole"}'

Or if you are running the floe command programmatically you can securely provide the credentials via a stdin pipe via --credentials=-:

echo '{"roleArn": "arn:aws:iam::111122223333:role/LambdaRole"}' | bundle exec ruby exe/floe --workflow my-workflow.asl --credentials -

Or you can pass a file path with the --credentials-file parameter:

bundle exec ruby exe/floe --workflow my-workflow.asl --credentials-file /tmp/20231218-80537-kj494t

If you need to set a credential at runtime you can do that by using the "ResultPath": "$.Credentials" directive, for example to user a username/password to login and get a Bearer token:

bundle exec ruby exe/floe --workflow my-workflow.asl --credentials='{"username": "user", "password": "pass"}'
{
  "StartAt": "Login",
  "States": {
    "Login": {
      "Type": "Task",
      "Resource": "docker://login:latest",
      "Credentials": {
        "username.$": "$.username",
        "password.$": "$.password"
      },
      "ResultPath": "$.Credentials",
      "Next": "DoSomething"
    },
    "DoSomething": {
      "Type": "Task",
      "Resource": "docker://do-something:latest",
      "Credentials": {
        "token.$": "$.bearer_token"
      },
      "End": true
    }
  }
}

Ruby Library

require 'floe'

workflow = Floe::Workflow.load("workflow.asl")
workflow.run!

You can also specify a specific docker runner and runner options:

require 'floe'

Floe::Workflow::Runner.docker_runner = Floe::Workflow::Runner::Podman.new
# Or
Floe::Workflow::Runner.docker_runner = Floe::Workflow::Runner::Kubernetes.new("namespace" => "default", "server" => "https://k8s.example.com:6443", "token" => "my-token")

workflow = Floe::Workflow.load("workflow.asl")
workflow.run!

Non-Blocking Workflow Execution

It is also possible to step through a workflow without blocking, and any state which would block will return Errno::EAGAIN.

require 'floe'

workflow = Floe::Workflow.load("workflow.asl")

# Step through the workflow while it would not block
workflow.run_nonblock

# Go off and do some other task

# Continue stepping until the workflow is finished
workflow.run_nonblock

You can also use the Floe::Workflow.wait class method to wait on multiple workflows and return all that are ready to be stepped through.

require 'floe'

workflow1 = Floe::Workflow.load("workflow1.asl")
workflow2 = Floe::Workflow.load("workflow2.asl")

running_workflows = [workflow1, workflow2]
until running_workflows.empty?
  # Wait for any of the running workflows to be ready (up to the timeout)
  ready_workflows = Floe::Workflow.wait(running_workflows)
  # Step through the ready workflows until they would block
  ready_workflows.each do |workflow|
    loop while workflow.step_nonblock == 0
  end
  # Remove any finished workflows from the list of running_workflows
  running_workflows.reject!(&:end?)
end

Docker Runner Options

Docker

Options supported by the Docker docker runner are:

  • network - What docker to connect the container to, defaults to "bridge". If you need access to host resources for development you can pass network=host.
  • pull-policy - Pull image policy. The default is missing. Allowed values: always, missing, never

Podman

Options supported by the podman docker runner are:

  • identity=string - path to SSH identity file, (CONTAINER_SSHKEY)
  • log-level=string - Log messages above specified level (trace, debug, info, warn, warning, error, fatal, panic)
  • network=string - What docker to connect the container to, defaults to "bridge". If you need access to host resources for development you can pass network=host.
  • noout=boolean - do not output to stdout
  • pull-policy=string - Pull image policy. The default is missing. Allowed values: always, missing, never, newer
  • root=string - Path to the root directory in which data, including images, is stored
  • runroot=string - Path to the 'run directory' where all state information is stored
  • runtime=string - Path to the OCI-compatible binary used to run containers
  • runtime-flag=stringArray - add global flags for the container runtime
  • storage-driver=string - Select which storage driver is used to manage storage of images and containers
  • storage-opt=stringArray - Used to pass an option to the storage driver
  • syslog=boolean - Output logging information to syslog as well as the console
  • tmpdir=string - Path to the tmp directory for libpod state content
  • transient-store=boolean - Enable transient container storage
  • volumepath=string - Path to the volume directory in which volume data is stored

Kubernetes

Options supported by the kubernetes docker runner are:

  • kubeconfig - Path to a kubeconfig file, defaults to KUBECONFIG environment variable or ~/.kube/config
  • kubeconfig_context - Context to use in the kubeconfig file, defaults to "default"
  • namespace - Namespace to use when creating kubernetes resources, defaults to "default"
  • pull-policy - Pull image policy. The default is Always. Allowed values: IfNotPresent, Always, Never
  • server - A kubernetes API Server URL, overrides anything in your kubeconfig file. If set KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT will be used
  • token - A bearer_token to use to authenticate to the kubernetes API, overrides anything in your kubeconfig file. If present, /run/secrets/kubernetes.io/serviceaccount/token will be used
  • ca_file - Path to a certificate-authority file for the kubernetes API, only valid if server and token are passed. If present /run/secrets/kubernetes.io/serviceaccount/ca.crt will be used
  • verify_ssl - Controls if the kubernetes API certificate-authority should be verified, defaults to "true", only vaild if server and token are passed

Development

After checking out the repo, run bin/setup to install dependencies. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ManageIQ/floe.

floe's People

Contributors

agrare avatar fryguy avatar kbrock avatar petergoldstein avatar renovate[bot] avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

floe's Issues

Support pluggable resources

Having alternative runner that live outside of floe would be a cool feature. For example, let's say in the ManageIQ application I wanted to run "builtin" methods, I would like to define, for example, builtin://email, or if I wanted to expose helpers for accessing data from our api, I would like to define vmdb://api/providers.

I believe this was initially implemented with a simple const_get, but I'd like to define these elsewhere and then "register" the resource type to a specific class, to avoid just loading any random constant.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

This repository currently has no open or pending branches.

Detected dependencies

bundler
Gemfile
github-actions
.github/workflows/ci.yaml
  • actions/checkout v4
  • ruby/setup-ruby v1
  • paambaati/codeclimate-action v6

  • Check this box to trigger a request for Renovate to run again on this repository

Add workflow validation

Ensure the workflow has the correct syntax.

Top-level fields

  • A State Machine MUST have an object field named "States", whose fields represent the states.
  • A State Machine MUST have a string field named "StartAt", whose value MUST exactly match one of names of the "States" fields. The interpreter starts running the the machine at the named state.

States

  • The state name, whose length MUST BE less than or equal to 80 Unicode characters, is the field name
  • State names MUST be unique within the scope of the whole state machine. (NOTE JSON.parse() only returns the last entry)
  • All states MUST have a "Type" field. This document refers to the values of this field as a state’s type, and to a state such as the one in the example above as a Task State.
  • Any state except for Choice, Succeed, and Fail MAY have a field named "End" whose value MUST be a boolean. The term "Terminal State" means a state with with { "End": true }, or a state with { "Type": "Succeed" }, or a state with { "Type": "Fail" }.

Transitions

  • All non-terminal states MUST have a "Next" field, except for the Choice State. The value of the "Next" field MUST exactly and case-sensitively match the name of another state.
  • Choice "Default" MUST match the name of another state.
  • Choice Choices MUST have a "Next" field that MUST match the name of another state.

Timestamps

  • These are strings which MUST conform to the RFC3339 profile of ISO 8601, with the further restrictions that an uppercase "T" character MUST be used to separate date and time, and an uppercase "Z" character MUST be present in the absence of a numeric time zone offset, for example "2016-03-14T01:59:00Z".

Data

  • The interpreter passes data between states to perform calculations or to dynamically control the state machine’s flow. All such data MUST be expressed in JSON.
  • As each state is executed, it receives a JSON text as input and can produce arbitrary output, which MUST be a JSON text.

Reference Paths

  • all Reference Paths MUST be unambiguous references to a single value, array, or object (subtree).

Payload Template

  • A Payload Template MUST be a JSON object; it has no required fields. (I believe this is covered already)
  • If the field value begins with only one "$", the value MUST be a Path.
  • If the field value begins with "$$", the first dollar sign is stripped and the remainder MUST be a Path
  • If the field value does not begin with "$", it MUST be an Intrinsic Function (see below)
  • A JSON object MUST NOT have duplicate field names after fields ending with the characters ".$" are renamed to strip the ".$" suffix. (I believe this is covered already)

Intrinsic Functions

  • An Intrinsic Function MUST be a string.
  • The Intrinsic Function MUST begin with an Intrinsic Function name.
  • An Intrinsic Function name MUST contain only the characters A through Z, a through z, 0 through 9, ".", and "_".
  • The Intrinsic Function name MUST be followed immediately by a list of zero or more arguments, enclosed by "(" and ")", and separated by commas.
  • Intrinsic Function arguments may be strings enclosed by apostrophe (') characters, numbers, null, Paths, or nested Intrinsic Functions.
  • The following characters are reserved for all Intrinsic Functions and MUST be escaped: ' { } \

Fix Task output and error handling for docker resources

When a docker resource is completed we use the container logs to feed the output / error handling of the Task state.

Currently the entirety of stdout+stderr is considered but this breaks down quickly when you consider that other libraries might be logging to stdout/stderr and also that you want to log progress during a long running container and only print output at the end.

We are suggesting that a delimiter be printed once a container has completed and is printing output for task output consideration and everything after that point will be considered for task output.

Pod names might overload the 63 character limit

Extracted from #132 (comment)

I think there's technically a prior bug here since the pod_name can only be 63 characters, and it's possible to have an image name longer than (63 - 2 - 4 - 36 =) 21 characters. Before this change, we could afford 26 characters, which is still pretty small.

I don't think we need a full UUID either - thinking we might be able to use, say, 8 or 12 random hex bytes.

SecureRandom.uuid is overly large for what we require, so I'm thinking we can use something significantly smaller, but wanted to discuss before I wrote a PR.

I'm thinking something like SecureRandom.urlsafe_base64(6) which should give us more than enough entropy in a tiny payload.

method chars bits example notes
SecureRandom.uuid 36 128 b6bb2535-808a-40bc-a3b1-cd71213c4d4d 36 = 32 chars + 4 -
SecureRandom.urlsafe_base64(16) 22 128 VKQxaJwgrmJkDTrjCj6NFA
SecureRandom.hex(6) 12 48 f788b7d2f448
SecureRandom.urlsafe_base64(8) 11 64 sBfQEpPiFVA I think this is what YouTube uses
SecureRandom.urlsafe_base64(6) 8 48 PHEghZQl *my choice
SecureRandom.hex(4) 8 32 c3b17c47

We don't actually need uuid's full 128 bits of entropy (2128 or 340 undecillion), since we're just trying to avoid collisions on very short-lived things. IMO 48 bits of entropy (248 or 281 trillion) is way more than enough.

@agrare @kbrock Just want to come to an agreement here, then I'll write up the PR.

Do not allow --net host

Extracted from the discussion in #52 (comment)


@Fryguy
As discussed, we should avoid --net host

it will definitely require rewriting some of the examples, so I'm ok to merge for now, but then we need a followup issue to remove it.

@agrare
Yeah we should probably discuss this separately since this isn't new code.

Docker isn't used for production only development and localhost:3000 is 99% of the development use cases so we can remove this if we want but it will require significant changes for developers' workflows

I get not everyone would use docker only for development so we could add a runner option set by the caller.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.