Overboard


Overboard: Hello world

In this tutorial we will create your first Overboard script and generate the Kubernetes YAML config to a file.

Requirements

  1. A Kubernetes cluster to learn on such as Kubernetes on Docker or minikube.
  2. A basic knowledge of using kubectl
  3. dotnet SDK installed
  4. Optionally, any IDE that supports F# (Visual Studio Code, IntelliJ Rider, Visual Studio, NeoVim)

Visual Studio Code with the Ionide is a great choice. See Setup your environment for more details.

Initial configuration

Create a F# script file called deployment.fsx

Copy the following code into the script file:

// TODO: import from Nuget
#r "nuget:Overboard"

// open the required namespaces
open Overboard
open Overboard.Common
open Overboard.Workload

// define your k8s config
let theInvalidDeployment = k8s {
    deployment {
        pod {
            container {
                name "nginx"
                image "nginx:latest"
                workingDir "/test-dir"
            }
        }
    }
}

// Write the YAML to infra.yaml file and get the list of validation issues
let validations = KubeCtlWriter.toYamlFile theInvalidDeployment "hello-world.yaml"
// Let's print out the validation errors
for err in validations do
    eprintfn "%s" err.Message

Output

"Deployment 'metadata.name' is required.
LabelSelector requires at least one of `matchLabels` or `matchExpressions`"

Now you can call the fsx file to generate your YAML config.

dotnet fsi deployment.fsx

If you run the apply command on your YAML file, you will see Kubernetes agrees with the validation errors.

kubectl apply -f hello-world.yaml

Fixed config

Let's address the validation errors that Overboard found.

  1. Call the add_matchLabel operation with a key/value pair for the label.
  2. Next, add the label to the pod using the _labels metadata operation, passing in a list of key/value pairs.
let theValidDeployment = k8s { 
    deployment {
        "test-deployment"
        replicas 2
        add_matchLabel ("app", "nginx") // <- fix the validation error
        pod {
            _labels [("app", "nginx")] // <- fix the validation error
            container {
                name "nginx"
                image "nginx:latest"
                workingDir "/test-dir"
            }
        }
    }
}

KubeCtlWriter.toYamlFile theValidDeployment "hello-world.yaml"

Output

Our validation errors are gone and we have a valid Kubernetes configuration.

"apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-deployment
  namespace: default
spec:
  minReadySeconds: 0
  progressDeadlineSeconds: 600
  replicas: 2
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
      namespace: default
    spec:
      containers:
      - image: nginx:latest
        name: nginx
        resources:
          limits:
            cpu: 1000m
            memory: 512Mi
          requests:
            cpu: 500m
            memory: 256Mi
        workingDir: /test-dir
      restartPolicy: Always

"

Testing the result

Let's execute this deployment against our Kubernetes cluster to confirm it is indeed correct.

dotnet fsi deployment.fsx
kubectl apply -f hello-world.yaml
kubectl get deployments

You should get the message: deployment.apps/test-deployment created

Summary

In this tutorial you created your first Overboard script and generated the YAML. We saw how to get and print out the validation errors. Finally, we saw how we can successfully deploy our generated script.

Congratulations! You have taken a turn toward a new way of configuring your infrastructure.

namespace Overboard
namespace Overboard.Common
namespace Overboard.Workload
val theInvalidDeployment: K8s
val k8s: K8sBuilder
val deployment: DeploymentBuilder
val pod: PodBuilder
val container: ContainerBuilder
<summary> A single application container that you want to run within a pod. https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#Container </summary>
custom operation: name (string) Calls ContainerBuilder.Name
custom operation: image (string) Calls ContainerBuilder.Image
custom operation: workingDir (string) Calls ContainerBuilder.WorkingDir
val validations: ValidationProblem list
module KubeCtlWriter from Overboard.K8s
val toYamlFile: k8s: K8s -> filePath: string -> ValidationProblem list
val err: ValidationProblem
val eprintfn: format: Printf.TextWriterFormat<'T> -> 'T
<summary>Print to <c>stderr</c> using the given format, and add a newline.</summary>
<param name="format">The formatter.</param>
<returns>The formatted result.</returns>
<example>See <c>Printf.eprintfn</c> (link: <see cref="M:Microsoft.FSharp.Core.PrintfModule.PrintFormatLineToError``1" />) for examples.</example>
property ValidationProblem.Message: string with get
val output1: K8sOutput
val toYaml: k8s: K8s -> K8sOutput
val errorOutput1: string
K8sOutput.errors: ValidationProblem list
Multiple items
module List from Microsoft.FSharp.Collections
<summary>Contains operations for working with values of type <see cref="T:Microsoft.FSharp.Collections.list`1" />.</summary>
<namespacedoc><summary>Operations for collections such as lists, arrays, sets, maps and sequences. See also <a href="https://docs.microsoft.com/dotnet/fsharp/language-reference/fsharp-collection-types">F# Collection Types</a> in the F# Language Guide. </summary></namespacedoc>


--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T member IsEmpty: bool member Item: index: int -> 'T with get ...
<summary>The type of immutable singly-linked lists.</summary>
<remarks>Use the constructors <c>[]</c> and <c>::</c> (infix) to create values of this type, or the notation <c>[1;2;3]</c>. Use the values in the <c>List</c> module to manipulate values of this type, or pattern match against the values directly. </remarks>
<exclude />
val map: mapping: ('T -> 'U) -> list: 'T list -> 'U list
<summary>Builds a new collection whose elements are the results of applying the given function to each of the elements of the collection.</summary>
<param name="mapping">The function to transform elements from the input list.</param>
<param name="list">The input list.</param>
<returns>The list of transformed elements.</returns>
<example id="map-1"><code lang="fsharp"> let inputs = [ "a"; "bbb"; "cc" ] inputs |&gt; List.map (fun x -&gt; x.Length) </code> Evaluates to <c>[ 1; 3; 2 ]</c></example>
val toSeq: list: 'T list -> seq<'T>
<summary>Views the given list as a sequence.</summary>
<param name="list">The input list.</param>
<returns>The sequence of elements in the list.</returns>
<example id="toseq-1"><code lang="fsharp"> let inputs = [ 1; 2; 5 ] inputs |&gt; List.toSeq </code> Evaluates to <c>seq { 1; 2; 5 }</c>. </example>
val arr: seq<string>
Multiple items
module String from Overboard

--------------------
module String from Microsoft.FSharp.Core
<summary>Functional programming operators for string processing. Further string operations are available via the member functions on strings and other functionality in <a href="http://msdn2.microsoft.com/en-us/library/system.string.aspx">System.String</a> and <a href="http://msdn2.microsoft.com/library/system.text.regularexpressions.aspx">System.Text.RegularExpressions</a> types. </summary>
<category>Strings and Text</category>
val concat: sep: string -> strings: seq<string> -> string
<summary>Returns a new string made by concatenating the given strings with separator <c>sep</c>, that is <c>a1 + sep + ... + sep + aN</c>.</summary>
<param name="sep">The separator string to be inserted between the strings of the input sequence.</param>
<param name="strings">The sequence of strings to be concatenated.</param>
<returns>A new string consisting of the concatenated strings separated by the separation string.</returns>
<exception cref="T:System.ArgumentNullException">Thrown when <c>strings</c> is null.</exception>
<example id="concat-1"><code lang="fsharp"> let input1 = ["Stefan"; "says:"; "Hello"; "there!"] input1 |&gt; String.concat " " // evaluates "Stefan says: Hello there!" let input2 = [0..9] |&gt; List.map string input2 |&gt; String.concat "" // evaluates "0123456789" input2 |&gt; String.concat ", " // evaluates "0, 1, 2, 3, 4, 5, 6, 7, 8, 9" let input3 = ["No comma"] input3 |&gt; String.concat "," // evaluates "No comma" </code></example>
namespace System
type Environment = static member Exit: exitCode: int -> unit static member ExpandEnvironmentVariables: name: string -> string static member FailFast: message: string -> unit + 1 overload static member GetCommandLineArgs: unit -> string[] static member GetEnvironmentVariable: variable: string -> string + 1 overload static member GetEnvironmentVariables: unit -> IDictionary + 1 overload static member GetFolderPath: folder: SpecialFolder -> string + 1 overload static member GetLogicalDrives: unit -> string[] static member SetEnvironmentVariable: variable: string * value: string -> unit + 1 overload static member CommandLine: string ...
<summary>Provides information about, and means to manipulate, the current environment and platform. This class cannot be inherited.</summary>
property System.Environment.NewLine: string with get
<summary>Gets the newline string defined for this environment.</summary>
<returns><see langword="\r\n" /> for non-Unix platforms, or <see langword="\n" /> for Unix platforms.</returns>
val theValidDeployment: K8s
custom operation: replicas (int) Calls DeploymentBuilder.Replicas
custom operation: add_matchLabel (string * string) Calls DeploymentBuilder.MatchLabel
<summary> Add a single label selector to the Deployment. </summary>
custom operation: _labels ((string * string) list) Calls PodBuilder.Labels
<summary> Labels for the Pod </summary>
val output2: K8sOutput
val content2: string
K8sOutput.content: string