---
slug: hello-holos
title: Hello Holos
description: Configure a simple Hello World service with Holos.
sidebar_position: 30
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import RenderingOverview from '@site/src/diagrams/rendering-overview.mdx';
import PlatformSequence from '@site/src/diagrams/render-platform-sequence.mdx';
import ComponentSequence from '@site/src/diagrams/render-component-sequence.mdx';

# Hello Holos

## Overview

One of the first exercises we do when learning a new programming language is
printing out a "Hello World!" greeting. Hello Holos configures the [podinfo Helm
chart][podinfo] producing a similar greeting from a Kubernetes Service.

By the end of this tutorial you have an understanding of how to wrap a Helm
Chart as a Holos Component.

## The Code

### Generating the structure

Use `holos` to generate a minimal platform directory structure.  Start by
creating a blank directory to hold the platform configuration.

```shell
mkdir holos-tutorial && cd holos-tutorial
```

Use the `holos init platform` command to initialize a minimal platform in the
blank directory.

```shell
holos init platform v1alpha5
```

Here's the filesystem tree we'll build in this tutorial.

<Tabs groupId="80D04C6A-BC83-44D0-95CC-CE01B439B159">
<TabItem value="tree" label="Tree">
```text showLineNumbers
holos-tutorial/
├── components/
│   └── podinfo/
│       └── podinfo.cue
├── cue.mod/
├── platform/
│   ├── platform.gen.cue
│   └── podinfo.cue
├── resources.cue
├── schema.cue
└── tags.cue
```
</TabItem>
<TabItem value="details" label="Details">
<div style={{display: "flex"}}>
<div>
```text showLineNumbers
holos-tutorial/
├── components/
│   └── podinfo/
│       └── podinfo.cue
├── cue.mod/
├── platform/
│   ├── platform.gen.cue
│   └── podinfo.cue
├── resources.cue
├── schema.cue
└── tags.cue
```
</div>
<div>
- **Line 1** The platform root is the `holos-tutorial` directory we created.
- **Line 2** This tutorial places components in `components/`.  They may reside
anywhere.
- **Line 3** A component is a collection of `*.cue` files at a path.
- **Line 4** We'll create this file and configure the podinfo helm chart in the
next section.
- **Line 5** The CUE module directory.  Schema definitions for Kubernetes and
Holos resources reside within the `cue.mod` directory.
- **Line 6** The platform directory is the **main entrypoint** for the `holos
render platform` command.
- **Line 7** `platform.gen.cue` is initialized by `holos init platform` and
contains the Platform spec.
- **Line 8** `podinfo.cue` integrates podinfo with the platform by adding the
component to the platform spec.  We'll add ths file after the next section.
- **Line 9** `resources.cue` Defines the Kubernetes resources available to
manage in CUE.
- **Line 10** `schema.cue` Defines the configuration common to all component
kinds.
- **Line 11** `tags.cue` Defines where component parameter values are injected
into the overall platform configuration.  We don't need to be concerned with
this file until we cover component parameters.
- **Lines 9-11** Initialized by `holos init platform`, user editable after
initialization.
</div>
</div>
</TabItem>
</Tabs>

### Creating a component

Start by creating a directory for the `podinfo` component.  Create an empty file
and then add the following CUE configuration to it.

<Tabs groupId="tutorial-hello-podinfo-helm-cue-code">
  <TabItem value="components/podinfo/podinfo.cue" label="Podinfo Helm Chart">
```bash
mkdir -p components/podinfo
touch components/podinfo/podinfo.cue
```
```cue showLineNumbers
package holos

// Produce a helm chart build plan.
holos: HelmChart.BuildPlan

HelmChart: #Helm & {
	Name: "podinfo"
	Chart: {
		version: "6.6.2"
		repository: {
			name: "podinfo"
			url:  "https://stefanprodan.github.io/podinfo"
		}
	}
	// Holos marshals Values into values.yaml for Helm.
	Values: {
		// message is a string with a default value.  @tag indicates a value may
		// be injected from the platform spec component parameters.
		ui: {
			message: string | *"Hello World" @tag(greeting, type=string)
		}
	}
}
```
  </TabItem>
</Tabs>

:::important
CUE loads all of `*.cue` files in the component directory to define component,
similar to Go packages.
:::

:::note
CUE _also_ loads all `*.cue` files from the component leaf directory to the
platform root directory.  In this example, `#Helm` on line 6 is defined in
`schema.cue` at the root.
:::

### Integrating the component

Integrate the `podinfo` component into the platform by creating a new cue file
in the `platform` directory with the following content.

<Tabs groupId="tutorial-hello-register-podinfo-component">
  <TabItem value="platform/podinfo.cue" label="Register Podinfo">
```bash
touch platform/podinfo.cue
```
```cue showLineNumbers
package holos

Platform: Components: podinfo: {
	name: "podinfo"
	path: "components/podinfo"
	// Inject a value into the component.
	parameters: greeting: "Hello Holos!"
}
```
  </TabItem>
</Tabs>

:::tip
Component parameters may have any name as long as they don't start with
`holos_`.
:::

## Rendering manifests

Render a manifest for `podinfo` using the `holos render platform ./platform`
command.  The `platform/` directory is the main entrypoint for this command.

<Tabs groupId="E150C802-7162-4FBF-82A7-77D9ADAEE847">
  <TabItem value="command" label="Command">
```bash
holos render platform ./platform
```
  </TabItem>
  <TabItem value="output" label="Output">
```
cached podinfo 6.6.2
rendered podinfo in 1.938665041s
rendered platform in 1.938759417s
```
  </TabItem>
</Tabs>

:::important
Holos rendered the following manifest file by executing `helm template` after
caching `podinfo` locally.
:::

```txt
deploy/components/podinfo/podinfo.gen.yaml
```

<Tabs groupId="0E9C231D-D0E8-410A-A4A0-601842A086A6">
  <TabItem value="service" label="Service">
```yaml showLineNumbers
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: podinfo
    app.kubernetes.io/version: 6.6.2
    helm.sh/chart: podinfo-6.6.2
  name: podinfo
spec:
  ports:
  - name: http
    port: 9898
    protocol: TCP
    targetPort: http
  - name: grpc
    port: 9999
    protocol: TCP
    targetPort: grpc
  selector:
    app.kubernetes.io/name: podinfo
  type: ClusterIP
```
  </TabItem>
  <TabItem value="deployment" label="Deployment">
```yaml showLineNumbers
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: podinfo
    app.kubernetes.io/version: 6.6.2
    helm.sh/chart: podinfo-6.6.2
  name: podinfo
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: podinfo
  strategy:
    rollingUpdate:
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      annotations:
        prometheus.io/port: "9898"
        prometheus.io/scrape: "true"
      labels:
        app.kubernetes.io/name: podinfo
    spec:
      containers:
      - command:
        - ./podinfo
        - --port=9898
        - --cert-path=/data/cert
        - --port-metrics=9797
        - --grpc-port=9999
        - --grpc-service-name=podinfo
        - --level=info
        - --random-delay=false
        - --random-error=false
        env:
        - name: PODINFO_UI_MESSAGE
          value: Hello Holos!
        - name: PODINFO_UI_COLOR
          value: '#34577c'
        image: ghcr.io/stefanprodan/podinfo:6.6.2
        imagePullPolicy: IfNotPresent
        livenessProbe:
          exec:
            command:
            - podcli
            - check
            - http
            - localhost:9898/healthz
          failureThreshold: 3
          initialDelaySeconds: 1
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 5
        name: podinfo
        ports:
        - containerPort: 9898
          name: http
          protocol: TCP
        - containerPort: 9797
          name: http-metrics
          protocol: TCP
        - containerPort: 9999
          name: grpc
          protocol: TCP
        readinessProbe:
          exec:
            command:
            - podcli
            - check
            - http
            - localhost:9898/readyz
          failureThreshold: 3
          initialDelaySeconds: 1
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 5
        resources:
          limits: null
          requests:
            cpu: 1m
            memory: 16Mi
        volumeMounts:
        - mountPath: /data
          name: data
      terminationGracePeriodSeconds: 30
      volumes:
      - emptyDir: {}
        name: data
```
  </TabItem>
</Tabs>

Holos renders the component with the greeting injected from the platform spec.

```shell
grep -B2 Hello deploy/components/podinfo/podinfo.gen.yaml
```
```yaml
        env:
        - name: PODINFO_UI_MESSAGE
          value: Hello Holos!
```

## Breaking it down

We run `holos render platform ./platform` because the cue files in the platform
directory export a [Platform] resource to `holos`.  The platform directory is
the entrypoint to the platform rendering process.

Components are the building blocks for a Platform.  The `platform/podinfo.cue`
file integrates the `podinfo` Component with the Platform.

Holos requires two fields to integrate a component with the platform.

1. A unique name for the component.
2. The component path to the directory containing the cue files exporting a
`BuildPlan` defining the component.

Component parameters are optional.  They allow re-use of the same component.
Refer to the [Component Parameters] topic for more information.

<Tabs groupId="67C1EE71-3EA8-4568-9F6D-0072BA09FF12">
  <TabItem value="overview" label="Rendering Overview">
    Take a look at the other tabs for more detailed sequence diagrams.
    <RenderingOverview />
  </TabItem>
  <TabItem value="platform" label="Platform Sequence">
    <PlatformSequence />
  </TabItem>
  <TabItem value="component" label="Component Sequence">
    <ComponentSequence />
  </TabItem>
</Tabs>

## Next Steps

We've shown how to integrate one Helm chart to the Platform, but we haven't yet
covered multiple Helm charts.  Continue on with the next tutorial to learn how
Holos makes it easy to inject values into multiple components safely and easily.

[podinfo]: https://github.com/stefanprodan/podinfo
[CUE Module]: https://cuelang.org/docs/reference/modules/
[CUE Tags]: https://cuelang.org/docs/howto/inject-value-into-evaluation-using-tag-attribute/
[Platform]: ../api/author.md#Platform
[Component Parameters]: ../topics/component-parameters.mdx
