tiap

package module
v0.14.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 22, 2025 License: Apache-2.0 Imports: 30 Imported by: 0

README

tiap isn't app publisher

PkgGoDev GitHub build and test Coverage Go Report Card

tiap is a small Go module and CLI tool to easily create Industrial Edge .app files (packages) for continuous delivery. It does nothing more than pulling the required container images based on your app's composer project and finally bundling all up in an .app package. The .app file then can be imported by users into their IEM systems.

Features

  • simple to automatically download and use within your pipeline:

    # >>> consider pinning tiap to a specific release version <<<
    go run github.com/thediveo/tiap/cmd/tiap@latest \
      -o hellorld.app hellorldapp/
    
  • defaults to using git describe to set the app version, or set explicitly using --app-version $SEMVER. Even accepts v prefixed semvers and then drops the prefix.

  • talks to the Docker API socket, so there's no need to either reconfigure the Docker daemon in your dev system or in pipelines, or to fiddle around with socat to reroute a localhost TCP port to the Docker socker.

    However, in view of supporting IE apps for different (CPU) architectures we recommend to never package image files from the local daemon, but instead to only pull from a (remote) registry. For this, we recommend sticking to --pull-always.

    go run github.com/thediveo/tiap/cmd/tiap@latest \
      -o hellorld.app --pull-always hellorldapp/
    
  • environment variable interpolation in both the composer file, as well as the details.json (see details below).

  • no need to deal with stateful IE app publisher workspaces.

  • small footprint.

Compose Project Safety Checks

Please note that while tiap doesn't lint the Docker composer project, it still does the following safety checks:

  • rejecting :latest image references (yes, we're more strict than IE App Publisher here for reasons that still hurt),

  • enforcing mem_limit service configuration (as this seems to be the most common stumbling block in a survey of one sample).

Environment Variable Interpolation

tiap supports variable interpolation for both the composer file, as well as details.json. It supports:

  • $FOO
  • ${FOO}
    • ${FOO:-default} and ${FOO-default}
    • ${FOO:?err} and ${FOO?err}
    • ${FOO:+replacement} and ${FOO+replacement} (this is useful in such cases as adding optional newline padding when a certain addition text env var has been defined, et cetera.)

tiap interpolates using the environment variables it was started with; there is no support for .env files so far (PR welcome).

Please note in case of details.json:

  • versionNumber is always set via the --version CLI flag. If the value to --version is empty (""), tiap runs git describe on the current repository to find the most recent tag that is reachable from HEAD.
  • versionID is automatically determined from he SHA256 hash of --version and the repository name.
  • releaseNotes can be set using --release-notes and in this case no further interpolation occurs on the release notes; instead, the caller needs to interpolate any environment variables before executing tiap. But if --release-notes isn't used or an empty value ("") specified, then interpolation occurs immediately after reading details.json and the result is kept.

Note

[!IMPORTANT] As of v0.11.0, tiap creates the image tar-balls inside .app files in non-legacy format, in order to be able to properly process newer (base) images. This requires a moderately recent IEM and is known to work with IE(v)D versions 1.19 and later.

CLI

The command

tiap -h

outputs

tiap isn't app publisher, but packages Industrial Edge .app files anyway

Usage:
  tiap -o FILE [flags] APP-TEMPLATE-DIR

Flags:
      --app-version string     app semantic version, defaults to git describe
      --debug                  enable debug logging
  -h, --help                   help for tiap
  -H, --host string            Docker daemon socket to connect to (only if non-default and using local images)
  -i, --interpolate            interpolate env vars in compose project and detail.json
  -o, --out string             mandatory: name of app package file to write
  -p, --platform string        platform to build app for (default "linux/amd64")
      --pull-always            always pull image from remote registry, never use local images
      --release-notes string   release notes (interpreted as double-quoted Go string literal; use \n, \", …)
  -v, --version                version for tiap

Hellorld Demo

This packages a hellorld.app: when deployed, it runs an HTTP server in a busybox container, serving the (in)famous "Hellorld!" greeting, but in text only.

# while in the toplevel directory of this repository:
go run github.com/thediveo/tiap/cmd/tiap@latest \
    -o hellorld.app --pull-always --interpolate testdata/app/
# note: --interpolate interpolates env vars in the compose project file.

outputs

2025-13-42T26:92:88Z INF tiap ... isn't app publisher version=(devel) license="Apache 2.0"
2025-13-42T26:92:88Z INF creating temporary project copy path=/tmp/tiap-project-2262943179
2025-13-42T26:92:88Z INF app repository detected repo=hellorld
2025-13-42T26:92:88Z INF normalized platform platform=linux/amd64
2025-13-42T26:92:88Z INF denormalized IE App architecture arch=x86-64
2025-13-42T26:92:88Z INF updated version ID based on semver semver=0.13.1-5-g7ab7cd8 versionId=Bor6mbv1fchBFRPTgFedPoFpjzuXgmK0
2025-13-42T26:92:88Z INF pulling images...
2025-13-42T26:92:88Z INF want image service=hellorld image=busybox:stable
2025-02-18T12:17:15Z INF written image contents amount=2156544 image-id=c107da89b447 duration=1s
2025-02-18T12:17:15Z INF images successfully pulled
2025-02-18T12:17:15Z INF writing final compose project...
2025-02-18T12:17:15Z INF final compose project written
2025-02-18T12:17:15Z INF wrapping up...
2025-02-18T12:17:15Z INF determining package files SHA256 digests...
2025-02-18T12:17:15Z INF digest(ed) path=detail.json digest=00d31aa9510fbf481b5d556c3b2ee0b08821f5948fbe48203ae703b800f4ac55
2025-02-18T12:17:15Z INF digest(ed) path=hellorld/appicon.png digest=77911f21764738f4c4b717f7bd0371cf752128754c7f0c30d181ee5ffd6adb27
2025-02-18T12:17:15Z INF digest(ed) path=hellorld/docker-compose.yml digest=4dde434ebf1a6450e74a63f4035974c4ce725abd9830142205913222a8725635
2025-02-18T12:17:15Z INF digest(ed) path=hellorld/images/c107da89b4470c4ed8fcaa56395fd11a0b85c57e1b6217e749655dfde1a9a91b.tar digest=558fc1edbf893ffb9abf1119dc0e4d236bdb6f165ff3d55f886aad66f82e0ea3
2025-02-18T12:17:15Z INF digest(ed) path=hellorld/nginx/nginx.json digest=d817b3a87c15f5c4807deb6e6ebf9e8aa2be5735c944f8b97a00124035c67cd5
2025-02-18T12:17:15Z INF creating IE app tar-ball doctor=Tarr professor=Fether
2025-02-18T12:17:15Z INF packaging path=detail.json
2025-02-18T12:17:15Z INF packaging path=digests.json
2025-02-18T12:17:15Z INF packaging path=hellorld
2025-02-18T12:17:15Z INF packaging path=hellorld/appicon.png
2025-02-18T12:17:15Z INF packaging path=hellorld/docker-compose.yml
2025-02-18T12:17:15Z INF packaging path=hellorld/images
2025-02-18T12:17:15Z INF packaging path=hellorld/images/c107da89b4470c4ed8fcaa56395fd11a0b85c57e1b6217e749655dfde1a9a91b.tar
2025-02-18T12:17:15Z INF packaging path=hellorld/nginx
2025-02-18T12:17:15Z INF packaging path=hellorld/nginx/nginx.json
2025-02-18T12:17:15Z INF IE app package successfully created
2025-02-18T12:17:15Z INF IE app package path=/tmp/hellorld.app duration=1s
2025-02-18T12:17:15Z INF removed temporary folder path=/tmp/tiap-project-2262943179

App Template

The recommended way to set up your app "template" structure to be used by tiap for packaging is to simply design your app project in IE App Publisher once and then immediately export it. Then unpack the .app file (it's a plain tar after all). Delete the images directory and digests.json. The rest should be checked into your git repository.

See also testdata/app for our canonical "Hellorld!" example.

The sweet size for app icons seem to be 150×150 pixels and they must be in PNG format.

App Architecture/Platform

In order to package an .app file for an architecture other than amd64 (cough x86-64 cough) use the --platform (or -p) flag. Its value can be a proper OCI platform specification, such as linux/amd64, or just an architecture specification like arm64. Aliases like x86-64 are understood and automatically normalized.

When packaging IE app files for multiple architectures we recommend – following Docker and OCI best practises – to only build multi-arch images and push them into a (sometimes private) registry. tiap will automatically pull the correct layers based on the platform setting.

Please note that tiap will default to the architecture tiap itself runs on, unless explicitly told otherwise using --platform!

Also, please note that the Industrial Edge platform requires the same app for multiple architectures to be fully separate apps: the "appId" in detail.json as well as the "app repository" (please see also the Creating a new Edge App documentation) thus must differ (the latter, for instance, hellorld and hellorld-arm64).

App Release Notes

The --release-note option interprets its value as a double-quoted Go string. To add newlines, use the \n escape or alternatively pass a literal newline (consult your shell on how to pass literal newlines). Double quotes must be always escaped as \".

However, be careful that your shell isn't messing around with your escaping on its own.

Tinkering

When tinkering with the tiap source code base, the recommended way is now a devcontainer environment. The devcontainer specified in this repository contains:

  • Docker-in-Docker
  • gocover command to run all tests with coverage, updating the README coverage badge automatically after successful runs.
  • Go package documentation is served in the background on port TCP/HTTP 6060 of the devcontainer.
  • go-mod-upgrade
  • goreportcard-cli.
  • pin-github-action for maintaining Github Actions.

Copyright 2023 Harald Albrecht, licensed under the Apache License, Version 2.0.

Documentation

Overview

Package tiap implements a simplistic Industrial Edge .app file packager.

“tiap isn't app publisher.” In can be used instead of the Siemens Industrial Edge App Publisher and iectl tools that are either interactive or an un-wget-able CLI tool. In contrast, tiap is easily “go install”-able, including version pinning. Moreover, tiap doesn't need setting up clean workspaces, et cetera.

All that tiap needs: a “template” folder with the usual app project folder structure. This can be easily gotten by exporting a (new) project once and purging out the image files and the digest.json file. The structure thus is as follows:

  • detail.json (but leave the versionNumber and versionId fields empty)
  • $REPO/
  • $REPO/appicon.png (150⨉150 pixels)
  • $REPO/docker-compose.yml (or .yaml)
  • $REPO/nginx (where necessary)
  • $REPO/nginx/nginx.json

Here, $REPO is an almost arbitrary directory name (except for “images”) that is considered to be the app's “repository” name.

Please note that tiap doesn't lint the Docker composer project, except for:

  • rejecting “:latest” image references (yes, we're more strict than IE App Publisher here for a reason),
  • enforcing “mem_limit” service configuration.

Index

Constants

View Source
const DefaultIEAppArch = "x86-64"

DefaultIEAppArch is the denormalized platform architecture name of the default "unnamed" architecture.

Variables

This section is empty.

Functions

func FileDigests

func FileDigests(root string) (map[string]string, error)

FileDigests calculates the SHA256 digests of files inside the “root” directory and its subdirectories, and returns them as a map of filenames to SHA256 hex strings. The SHA256 hex strings do not contain a “sha256:” digist scheme prefix.

Please note that symbolic links are ignored.

func SaveImageToFile

func SaveImageToFile(ctx context.Context,
	imageref string,
	platform string,
	savedir string,
	optclient daemon.Client,
) (filename string, err error)

SaveImageToFile checks if the referenced image (“imageref”) is either available locally for the specific platform or otherwise attempts to pull it, and then immediately saves it to local storage in the specified directory “savedir”. The name of the image file will be the image reference's SHA256. SaveImageToFile either reports success or a more specific error.

Please note that an attempt to find the referenced image with the local daemon is only made when a non-nil client has been passed in. Otherwise, always a pull is attempted only.

func WriteDigests

func WriteDigests(w io.Writer, root string) error

WriteDigests determines the file digests inside the “root” directory and its sub directories and then writes the results to the specified io.Writer in “digests.json” format.

Types

type App

type App struct {
	// contains filtered or unexported fields
}

App represents an IE App (project) to be packaged.

func NewApp

func NewApp(source string) (a *App, err error)

NewApp returns an IE App object initialized from the specified “template” path.

func (*App) Done

func (a *App) Done()

Done removes all temporary work files.

func (*App) Interpolate added in v0.12.0

func (a *App) Interpolate(vars map[string]string) error

Interpolate all variables in the app's composer project using the specified variables, updating the project's YAML data accordingly. In case of interpolation problems, it returns an error, otherwise nil.

func (*App) Package

func (a *App) Package(out string) error

Package (finally) packages the IE app project in a IE app package tar file indicated by “out”.

func (*App) PullAndWriteCompose

func (a *App) PullAndWriteCompose(
	ctx context.Context,
	platform string,
	optclient daemon.Client,
) error

PullAndWriteCompose analyzes the project's compose deployment in order to pull the required container images, then saves the images into the temporary stage, and writes composer project.

func (*App) SetDetails

func (a *App) SetDetails(semver string, releasenotes string, iearch string, vars map[string]string) error

SetDetails sets the semver (“versionNumber”, oh well) of this release, notes (if any) and optional architecture, and then writes a new “detail.json” into the build directory. This automatically sets the versionId to some suitable value behind the scenes. At least we think that it might be a suitable versionId value.

type ComposerProject

type ComposerProject struct {
	// contains filtered or unexported fields
}

ComposerProject represents a loaded Docker composer project.

func LoadComposerProject

func LoadComposerProject(dir string) (*ComposerProject, error)

LoadComposerProject looks in the specified “dir” for a Docker composer project file and loads it. This takes the several official variations of composer project file names into account. However, contrary to Docker's composer, it doesn't look into parent directories for project files and it doesn't take overrides into account.

func NewComposerProject

func NewComposerProject(path string) (*ComposerProject, error)

NewComposerProject reads the specified YAML file containing a (Docker) composer project and returns a ComposerProject object for it.

func (*ComposerProject) Images

func (p *ComposerProject) Images() (ServiceImages, error)

Images returns the mapping between services defined in this composer project and the container images they reference.

func (*ComposerProject) Interpolate added in v0.12.0

func (cp *ComposerProject) Interpolate(vars map[string]string) error

Interpolate all variables in this composer project using the specified variables, updating the project's YAML data accordingly. In case of interpolation problems, it returns an error, otherwise nil.

func (*ComposerProject) PullImages

func (p *ComposerProject) PullImages(
	ctx context.Context,
	serviceimgs ServiceImages,
	platform string,
	root string,
	optclient daemon.Client,
) error

PullImages takes a service-to-image reference mapping and pulls and saves the required container images. The caller is responsible to supply the correct "root" directory path inside which to place the images in a “image/” subdirectory. That is, the root path needs to reference the arbitrarily named “repository” folder.

func (*ComposerProject) Save

func (p *ComposerProject) Save(w io.Writer) error

Save writes the loaded composer project to the specified io.Writer, returning an error in case of failure.

type ServiceImages

type ServiceImages map[string]string

ServiceImages maps service names in Docker composer projects to their image references.

Directories

Path Synopsis
cmd
tiap
tiap isn't app publisher, but packages Industrial Edge .app files anyway.
tiap isn't app publisher, but packages Industrial Edge .app files anyway.
Package interpolate provides string interpolation from environment variables as per the Compose specification.
Package interpolate provides string interpolation from environment variables as per the Compose specification.
test
grab
Package grab provides temporarily grabbing the structured log output, in form of JSON output, and later restoring it to its original configuration.
Package grab provides temporarily grabbing the structured log output, in form of JSON output, and later restoring it to its original configuration.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL