Bringing `cowsay` into a Cloud Foundry world

Who doesn't love ASCII art and a CLI?  

One of the early *nix commands that I discovered (well the one that clearly sticks in my mind) was a tool called cowsay, which has its origins back in the late 90’s. cowsay is not a particularly useful tool however when embarking on learning how to create a Cloud Foundry CLI plugin for the first time it was the obvious choice!

First of all what does cowsay do? You can install via brew if you run a mac or a Google search for your other OS if you want to play yourself. In essence what you can do is get a cow to say something for you (some flags exist to think or pick another animal);

If you have used Cloud Foundry before you will be familiar with the CLI, if not more information can be found in the documentation. The CLI allows you to interact with the platform to carry out day to do functions.

The cool thing is that you can extend the functionality with plugins creating custom commands. A number of community based ones are available here however you can also create your own to solve specific use case.

Some of the plugins that I have either used or find cool are;

• cf top

Installing Plugins

It's straight forward to install plugins (well assuming you don't sit behind some corporate proxy or restricted access to you machine). The cf install plugin <name> can be used.

It is also easy to work out what plugins are available to you to install (other than looking at the website, and remember who doesn't love the CLI);

cf repo-plugins
Getting plugins from all repositories ...

Repository: CF-Community
name                            version   description
UpdateRouteWeightPlugin         0.0.1     A plugin for interacting with the mesh networking weighted routing feature.
push-with-vault                 0.0.4     cf plugin to push cf app with vault
cfdev                           0.0.15    Run Cloud Foundry in your local environment
conduit                         0.0.7     Makes it easy to directly connect to your remote service instances
Create-Service-Push             1.3.1     A CF CLI plugin to create services specified from a services manifest yml file and then push an application to Cloud Foundry
cf-puppeteer                    0.0.13    Perform a zero-downtime restage of an application over the top of an old one (autopilot fork)
html5-plugin                    1.2.0     CLI client for SAP Cloud Platform HTML5 Applications Repository service
MysqlTools                      0.7.1     Tool to migrate MySQL service instances from PCF MySQL v1 to PCF MySQL v2
log-stream                      0.3.0     A plugin to stream logs
report-memory-usage             0.1.0     This plugin used to report memory usage vs quota across all orgs / spaces / apps the user has access to.
report-disk-usage               0.1.0     This plugin used to report disk usage vs quota across all orgs / spaces / apps the user has access to.
log-cache                       2.1.0     Allows users to query Log Cache.
app-autoscaler-plugin           1.1.0     App-AutoScaler plug-in provides the command line interface to manage App AutoScaler service policies, retrieve metrics and scaling history.
bg-restage                      1.1.0     Perform a zero-downtime restage of an application over the top of an old one (highly inspired by autopilot)
sync                            1.2.1     Synchronize a local folder to a remote folder inside a Cloud Foundry app to make changes on the fly
metric-registrar                1.1.0     Allow users to register metric sources.
cf-recycle-plugin               1.1.1     Sequentially recycle application instances
spring-cloud-services           1.0.3     Manage Pivotal Spring Cloud Services service instances and service registries. Encrypt a string using a configuration service.
drains                          1.2.0     A plugin to simplify interactions with user provided syslog drains.
blue-green-deploy               1.4.0     Zero downtime deploys with smoke test support
maven-push                      0.4.0     Drop in replacement for `cf push` that downloads the artifact based on maven coordinates from the manifest, then pushes it.
psql-plugin                     1.0.0     Runs psql clients against your CF database services. Use it to inspect, dump and restore your DB.
report-buildpacks               0.1.0     This plugin used to report applications that use out-of-date buildpacks.
report-users                    0.6.0     This plugin used to report all users in your installation with their
                                          roles.

copyenv                         1.2.4     Expose remote VCAP_SERVICES and VCAP_APPLICATION on the client environment
mysql-plugin                    2.0.0     Runs mysql and mysqldump clients against your CF database services. Use it to inspect, dump and restore your DB.
log-noise                       1.5.1     A nozzle and CLI tool for identify high volume log producing applications
java                            2.0.0     Obtain a heap-dump or thread-dump from a running, Diego-enabled, SSH-enabled Java application.
cflocal                         0.19.0    Stage and launch CF apps, push and pull droplets, and connect to real CF services -- in Docker
app-metrics                     2.0.0     This plugin allows you to hit a metrics endpoint across all your app instances
Copy Autoscaler                 0.0.1     CF CLI Plugin to import/export Pivotal App Autoscaler Settings
service-use                     1.2.2     Report on service usage in the platform based on your sign-in privileges
top                             0.9.3     An interactive interface that shows top statistics similar in concept as the UNIX top command.
                                          .
                                          Fixed - Multithreaded updated of metadata cache caused crash.
                                          Fixed - Crash on resize of AboutView.
                                          Fixed - High refresh rate of UI causes crash.
                                          Fixed - Org/Space view was wrongly adding memory of stopped apps to totals.
                                          Enhancement - Incrementally use metadata as its loaded which improves usability with platforms that have large number of apps.

Service Instance Logging        1.0.1     For service brokers that implement a logging endpoint, this plugin will allow recent dump and streaming of logs from that endpoint.
spring-cloud-dataflow-for-pcf   1.0.0     Manage data pipelines on Spring Cloud Data Flow for PCF via Data Flow shell.
autopilot                       0.0.6     zero downtime deploy plugin for cf applications
Swisscom Application Cloud      0.1.1     The official Swisscom Application Cloud plugin gives you access to all the additional features of the App Cloud
apigee-broker-plugin            0.1.1     Allows users to interact with the Apigee Service Broker in an easier and more streamlined way
Firehose Plugin                 0.13.0    This plugin allows you to connect to the firehose
Targets                         1.2.0     Easily manage multiple CF targets
open                            1.2.1     Open App URLs + Service dashboards in browser
fastpush                        1.1.0     Fast, non-persistent, smart and incrementally updating of your application
started-apps                    0.1.0     Easily view which apps are started
cf-aklogin                      1.2.9     CF Login tool to switch between CF environments with a single command
deploy                          0.3.0     Create/update initial Cloud Foundry state (orgs, spaces, users, services, apps, and more) via a yml file
copy                            0.9.4     Copies applications and services in current space to another space or Cloud Foundry target.
whoami-plugin                   0.2.0     This plugin shows you the name of the currently logged in user
cf-icd-plugin                   0.0.11    Helper plugin for traceability function in IBM Cloud Devops
buildpack-usage                 1.0.4     Show which apps are using an installed buildpack
do-all                          1.1.2     Run the same command on all the apps in a space, org, or globally
get-events                      0.6.0     Get microservice events
route-lookup                    1.1.0     Find the application(s) associated with a given hostname
doctor                          1.0.3     doctor scans your deployed applications, routes and services for anomalies and reports any issues found. (CLI v6.7.0+)
Usage Report                    1.4.0     Report on memory usage and application instances for orgs and spaces
docker-usage                    1.0.3     Show which apps are using docker images
cf-download                     1.2.0     Plugin for downloading your application contents after staging.
update-cli                      0.1.1     Update cloudfoundry/cli to the latest version
cf-predix-analytics-plugin      0.0.1     Provides CLI based access to Predix Analytics Catalog features
antifreeze                      0.3.0     Detect if an app has unexpected ENV vars or services bound which are missing from the manifest
Scaleover                       1.0.4     Roll traffic from one app to another by scaling over time
cf-willitconnect                1.1.0     Can CF connect to a thing?
Download Droplet                1.0.1     Download droplets to your local machine
manifest-generator              1.0.0     Help you to generate a manifest from 0 (CLI v6.7.0+)
kibana-me-logs                  0.4.3     Launches the Kibana UI (from kibana-me-logs) for an application.
Cloud Deployment Plugin         1.0.1     This plugin allows you to manage application deployment details and manifests in a github repo (like spring cloud config server for deployments)
CLI-Recorder                    1.0.2     Records and playbacks CLI commands.
Buildpack Management            1.0.0     Provides a declarative way to configure system buildpacks in a Cloud Foundry installation
Statistics                      1.0.0     Display live metrics/statistics about an app
wildcard_plugin                 1.0.0     Allows user to search and delete apps using wildcard(*)
Buildpack Usage                 1.0.0     View all buildpacks used in the current CLI target context.

You can also add new plugin repositories (a private one or a more curated one) using the cf add-plugin-repo clijockey http://plugins.cfapps.io which can be searched with cf repo-plugins clijockey command.

Creating plugins

The plugins are written in Go and use an interface defined by the CF CLI project. The implementation looked straight forward so I decided to create a plugin to learn Go and also give me a better understanding of the CF CLI.

As the title suggests I decided to bring cowsay to the Cloud Foundry world.

An example plugin is available as a starting point, it demonstrates the structure and shows the three key elements;

  • run function
  • Main
  • Metadata

run

This is part of the plugin interface defined by the core CLI therefore must be implemented. It is the entry point when the core CLI is invoking a command the plugin has added.

func (c *Cowsay) Run(cliConnection plugin.CliConnection, args []string) {
	// Plugin Logic

}
  • plugin.CliConnection is a struct that can be used to invoke CLI commands.
  • args[0] will be the name of the command and followed by additional arguments typed in at the CLI

Main

Main is used to initialise the plugin as well as any dependencies required for the plugin. Apparently this is unlike most Golang programs, although this was my first attempt at writing solo with Go so doesn't mean much to me!

func main() {
	// Any initialization for your plugin can be handled here
	//
	// Note: to run the plugin.Start method, we pass in a pointer to the struct
	// implementing the interface defined at "code.cloudfoundry.org/cli/plugin/plugin.go"
	//
	// Note: The plugin's main() method is invoked at install time to collect
	// metadata. The plugin will exit 0 and the Run([]string) method will not be
	// invoked.
	plugin.Start(new(Cowsay))
	// Plugin code should be written in the Run([]string) method,
	// ensuring the plugin environment is bootstrapped.
}

Metadata

This defines the plugin's metadata that is used to identify it and provide some basic help. The information that you can define is;

  • Name of the plugin
  • Command syntax

The cowsay example is as follows, it is where we define the version information, help and how to execute the command.

func (c *Cowsay) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "cowsay",
		Version: plugin.VersionType{
			Major: 0,
			Minor: 12,
			Build: 0,
		},
		MinCliVersion: plugin.VersionType{
			Major: 6,
			Minor: 7,
			Build: 0,
		},
		Commands: []plugin.Command{
			{
				Name:     "cowsay",
				HelpText: "Plain old cowsay example for a little bit of fun!",

				// UsageDetails is optional
				// It is used to show help of usage of each command
				UsageDetails: plugin.Usage{
					Usage: "cf cowsay\n	cf cowsay <some text>\n cf cowsay space\n cf cowsay apps\n cf cowsay check\n cf cowsay whoami",
				},
			},
		},
	}
}

Now when you list the plugins install you will get the HelpText, Name and plugin.VersionType information.

cf plugins                                                                   
Listing installed plugins...

plugin   version   command name   command help
cowsay   0.12.0    cowsay         Plain old cowsay example for a little bit of fun!

Use 'cf repo-plugins' to list plugins in registered repos available to install.

The help command will also return the information entered into the plugin.Usage element.

cf cowsay -h                                                                 

NAME:
   cowsay - Plain old cowsay example for a little bit of fun!

USAGE:
 cf cowsay
 cf cowsay <some text>
 cf cowsay space
 cf cowsay apps
 cf cowsay check
 cf cowsay whoami

As of writing this the plugin has a few arguments;

  • space - provides details of the Org/space you are in
  • apps - list of apps in your current space
  • check - runs a quick check of the URL's to check for 200
  • whoami - who and where you are logged on
  • Text String - same as good old cowsay
cf cowsay "Hello clijockey, how are you today?"                                                                                                             
 _____________________________________
< Hello clijockey, how are you today? >
 -------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
cf cowsay space                                                                                                                                         
 ______________________________________
/ Space: redwards in the organisation: \
\ pivotal-emea-cso                     /
 --------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
cf cowsay apps                                                            
 ______________________________________
/ So `attendees` has stopped man!! 🔴  \
| So `hold-page` is all good ✅        |
| So `k8s-course` is all good ✅       |
| So `k8s-training` is all good ✅     |
| So `requesta` is all good ✅         |
\ So `vsts-cf-example` is all good ✅  /
 --------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
cf cowsay whoami                                                                                                                                        
 __________________________________________
/ You are logged in as redwards@pivotal.io \
| on https://api.run.pivotal.io            |
|                                          |
| Using org pivotal-emea-cso in the space  |
\ redwards                                 /
 ------------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

All of the code, and releases, are stored in my GitHub repo here.

Wrap up and whats next?

Hopefully you can see that it is easy to create plugins for the CF CLI ..... if a none programming infrastructure/ops chap can knock one together, a competent developer can create a much more sophisticated one :-).

I have a number of things planned to increase my knowledge and learning to date, my current thoughts are;

  • Test coverage for plugin development (yes I'm a bad human and should have started with the tests)
  • CI/CD Pipeline to take the code and create the Go binary stored in GitHub
  • Add some new animals, like a goose for example.
  • A conference talk around this
  • v3 of the CLI changes
Show Comments