June 20-22 Announcing HashiConf Europe full schedule: keynotes, sessions, labs & more Register Now
  • Infrastructure
    • terraform
    • packer
  • Networking
    • consul
  • Security
    • vault
    • boundary
  • Applications
    • nomad
    • waypoint
    • vagrant
  • HashiCorp Cloud Platform

    A fully managed platform to automate infrastructure on any cloud with HashiCorp products.

    • consul
    • terraform
    • vault
    • packerbeta
    Visit cloud.hashicorp.com
  • Overview
  • Tutorials
  • Docs
  • CLI
  • Plugins
  • Community
GitHub
Download
    • v0.8.x (latest)
    • v0.7.x
    • v0.6.x
    • v0.5.x
    • v0.4.x
    • v0.3.x
    • v0.2.x
    • v0.1.x
    • Overview
      • Overview
      • Helm
      • Heroku, Vercel, etc.
      • Kubernetes
  • Getting Started
    • Overview
    • Compatibility Promise
    • Protocol Version Table
    • Release Notifications
      • Overview
      • Upgrade to 0.2.0

    • Install
    • Externally Built Images
    • Building Container Images
    • Helm Deployment
    • YAML-Free Deployment
    • YAML Directory Deployment
    • Resource Status
    • ConfigMaps and Secrets

    • Overview
    • Git Integration
    • Remote Operations
    • Overview
    • Build
    • Deploy
    • Release
    • Hooks
    • Labels
    • Workspace and Label Scoping
    • Overview
      • Overview
      • Input Variables
      • External Data
      • artifact
      • deploy
      • entrypoint
      • labels
      • path
      • workspace
      • Overview
      • Full Reference
      • Templating
      • Overview
      • Expressions
      • JSON Syntax
    • app
    • build
    • config
    • deploy
    • hook
    • plugin
    • registry
    • release
    • runner
    • url
    • use
    • variable
  • URL Service
  • Logs
  • Exec
    • Overview
    • Dynamic Values
    • Files
    • Internal Values
    • Workspace and Label Scoping
    • Overview
      • Overview
      • OIDC
      • Overview
      • Maintenance
      • Production
      • Security
    • Express Server Install
    • Overview
    • Configuration
    • Profiles
    • On-Demand Runner
    • Additional Runners
  • Workspaces
  • Plugins
  • Triggers

    • Overview
      • Overview
      • Registering Plugin Components
      • Handling Configuration
      • Implementing the Builder Interface
      • Compiling the Plugin
      • Creating an Example Application
      • Testing the Plugin
    • Initializing the SDK
    • Passing Values Between Components
      • Overview
      • Authenticator
      • Configurable
      • ConfigurableNotify
      • Builder
      • Registry
      • Platform
      • ReleaseManager
      • Destroy
      • Status
      • Default Parameters
      • Overview
    • Overview
    • Disable
    • Overview
    • GitHub Actions
    • GitLab CI/CD
    • CircleCI
    • Jenkins
  • Troubleshooting
  • Glossary

    • Overview
    • Architecture
    • Operation Execution
  • Roadmap
Type '/' to Search

»Implementing the Builder Interface

Video tutorial below:

Once the optional configuration has been completed, you can then implement the BuildFunc method as defined on the Builder interface. BuildFunc has a single return parameter which is an interface representing a function called by Waypoint when running the waypoint build command.

BuildFunc() interface{}
BuildFunc() interface{}

The function to be called does not strictly correspond to any signature for the input parameters. Waypoint functions have their parameters dynamically injected at runtime. The list of available parameters can be found in the Default Parameters documentation.

While you can choose the input parameters for your BuildFunc, Waypoint enforces specific output parameters. These return parameters must be of types proto.Message, and error. The proto.Message is a struct which implements the Protocol Buffers Message interface (github.com/golang/protobuf/proto). Waypoint uses Protocol Buffers to pass messages between the different stages of the workflow and serialize data to the internal data store. The error, which is the second part of the tuple, determines if your build stage has succeeded or failed.

The default function created by the template, has created a BuildFunc which looks like the following example:

func (b *Builder) build(ctx context.Context, ui terminal.UI) (*Binary, error) {
    u := ui.Status()
    defer u.Close()
    u.Update("Building application")

    return &Binary{}, nil
}
func (b *Builder) build(ctx context.Context, ui terminal.UI) (*Binary, error) {
    u := ui.Status()
    defer u.Close()
    u.Update("Building application")

    return &Binary{}, nil
}

This function contains the following input parameters:

  • context.Context - Used to check if the server has canceled the build.
  • terminal.UI - Used to write output and request input from the Waypoint CLI.

The output parameters are defined as:

  • *Binary - Generated struct from output.proto
  • error - Returning a non-nil error terminates execution and presents the error to the user.

»Output Values

Output Values such as Binary in Waypoint plugins need to be serializable to Protocol Buffer binary format. To enable this, you do not directly define the Go struct. Instead, you describe this as a Protocol Buffer message and use the protoc command to generate the code. If you take a look at the output.proto file, you will see the following message defined.

message Binary {
  string location = 1;
}
message Binary {
  string location = 1;
}

When the protoc command runs, it generates Go code from this Protocol Buffer definition. You can see the output of this in the output.pb.go file:

type Binary struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
}
type Binary struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
}

For this guide, you do not need to change Binary; however, if you would like to learn more about passing values between components, please see the specific documentation.

»Creating the Build Process

Let’s look at the implementation of the build method. You will need to provide status updates to the user of your plugin. To do this, you can use terminal.UI; this allows you to write output to the terminal using a common UX across all Waypoint plugins.

The method ui.Status() requests a live updating status; you use this to write updates to the Waypoint terminal output. When finished with Status you should always close a Status, usingst.Close(); this ensures that the status is updated correctly in the terminal. Finally, in the example, the Status is updated with the message "Building application". Unlike a scrolling log output, Waypoint allows the user to focus on the critical information at the current time. The terminal.Status allows you to replace content already written to the terminal, which may no longer be relevant.

  u := ui.Status()
  defer st.Close()

  u.Update("Building application")
  u := ui.Status()
  defer st.Close()

  u.Update("Building application")

After the Update call, let's add some code to the build method, which will set the configuration's defaults if they are not set.

  // setup the defaults
  if b.config.OutputName == "" {
    b.config.OutputName = "app"
  }

  if b.config.Source == "" {
    b.config.Source = "./"
  }
  // setup the defaults
  if b.config.OutputName == "" {
    b.config.OutputName = "app"
  }

  if b.config.Source == "" {
    b.config.Source = "./"
  }

Now all the defaults are set up; you can add the code which will build the application. Because the plugin runs in your current user space, you can use the Go standard library exec package to shell out a process and run go build. Set the values from the config for the source and the application name, and finally, run the command.

c := exec.Command(
     "go",
     "build",
     "-o",
     b.config.OutputName,
     b.config.Source,
)

err := c.Run()
c := exec.Command(
     "go",
     "build",
     "-o",
     b.config.OutputName,
     b.config.Source,
)

err := c.Run()

»Writing to the Terminal Output

If an error occurs during the build process, you can update the terminal output to show a failure message. The error returned from the function will also be output to the terminal by Waypoint. The Step command can be used to write the status of the current component if an error occurs during the build command by calling the Step method.

Add the following code to your build function after the Run method.

if err != nil {
  u.Step(terminal.StatusError, "Build failed")

  return nil, err
}
if err != nil {
  u.Step(terminal.StatusError, "Build failed")

  return nil, err
}

Would result in the terminal output shown to the user like:

» Building...
x Build failed
» Building...
x Build failed

Finally, if the build succeeds, you can update the status and return the proto.Message that can be passed to the next step. Add the following code to your build function after the error check.

  u.Step(terminal.StatusOK, "Application built successfully")

  return &Binary{
    Location: path.Join(b.config.Source, b.config.OutputName),
  }, nil
  u.Step(terminal.StatusOK, "Application built successfully")

  return &Binary{
    Location: path.Join(b.config.Source, b.config.OutputName),
  }, nil

The code for the plugin is now complete; let's see how to build and test it.

Next - Compiling the Plugin

github logoEdit this page

Using Waypoint

The best way to understand what Waypoint can enable for your projects is to give it a try.

Waypoint tutorials
Waypoint documentation
Tutorial

Get Started - Kubernetes

Build, deploy, and release applications to a Kubernetes cluster.

View
Tutorial

Introduction to Waypoint

Waypoint enables you to publish any application to any platform with a single file and a single command.

View

Waypoint is maintained by HashiCorp, Inc.

View Code of Conduct
DocumentationCLI ReferenceTutorialsIntegrations
All systems normal