Skip to content

Commit

Permalink
Enable libexec artifacts (#408)
Browse files Browse the repository at this point in the history
* Enable libexec artifacts

This commit allows the user to specify artifacts to be installed under
`usr/libexec`. I used the following documentation as a guide:

https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04s07.html#:~:text=Purpose,subdirectory%20under%20%2Fusr%2Flibexec%20.

Below, you can see at a glance what the behavior of various settings
will be. The following examples assume we are building a spec with
top-level `name: docker`. The subpath field defaults to the package
name.

```yaml
 # goes to /usr/libexec/docker/docker-compose
libexec:
  bin/docker-compose:

 # goes to /usr/libexec/docker/cli-plugins/docker-compose
libexec:
  bin/docker-compose:
    subpath: docker/cli-plugins

 # goes to /usr/libexec/something_else/cli-plugins/docker-compose
libexec:
  bin/docker-compose:
    subpath: something_else/cli-plugins

 # goes to /usr/libexec/docker/cli-plugins/hello
libexec:
  bin/docker-compose:
    subpath: docker/cli-plugins
    name: hello

 # goes to /usr/libexec/docker/hello
libexec:
  bin/docker-compose:
    name: hello

```

Signed-off-by: Peter Engelbert <pmengelbert@gmail.com>
  • Loading branch information
pmengelbert authored Oct 23, 2024
1 parent 19eedbc commit eb8c95c
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 0 deletions.
2 changes: 2 additions & 0 deletions artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
type Artifacts struct {
// Binaries is the list of binaries to include in the package.
Binaries map[string]ArtifactConfig `yaml:"binaries,omitempty" json:"binaries,omitempty"`
// Libexec is the list of additional binaries that may be invoked by the main package binary.
Libexec map[string]ArtifactConfig `yaml:"libexec,omitempty" json:"libexec,omitempty"`
// Manpages is the list of manpages to include in the package.
Manpages map[string]ArtifactConfig `yaml:"manpages,omitempty" json:"manpages,omitempty"`
// DataDirs is a list of read-only architecture-independent data files, to be placed in /usr/share/
Expand Down
7 changes: 7 additions & 0 deletions docs/spec.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@
"type": "object",
"description": "Binaries is the list of binaries to include in the package."
},
"libexec": {
"additionalProperties": {
"$ref": "#/$defs/ArtifactConfig"
},
"type": "object",
"description": "Libexec is the list of additional binaries that may be invoked by the main package binary."
},
"manpages": {
"additionalProperties": {
"$ref": "#/$defs/ArtifactConfig"
Expand Down
10 changes: 10 additions & 0 deletions frontend/deb/debroot.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,16 @@ func createInstallScripts(worker llb.State, spec *dalec.Spec, dir string) []llb.
}
}

if len(spec.Artifacts.Libexec) > 0 {
sorted := dalec.SortMapKeys(spec.Artifacts.Libexec)
for _, key := range sorted {
cfg := spec.Artifacts.Libexec[key]
resolved := cfg.ResolveName(key)
targetDir := filepath.Join(`/usr/libexec`, cfg.SubPath)
writeInstall(key, targetDir, resolved)
}
}

if len(spec.Artifacts.Libs) > 0 {
sorted := dalec.SortMapKeys(spec.Artifacts.Libs)
for _, key := range sorted {
Expand Down
18 changes: 18 additions & 0 deletions frontend/rpm/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,14 @@ func (w *specWrapper) Install() fmt.Stringer {
}
}

if w.Spec.Artifacts.Libexec != nil {
libexecFileKeys := dalec.SortMapKeys(w.Spec.Artifacts.Libexec)
for _, k := range libexecFileKeys {
le := w.Spec.Artifacts.Libexec[k]
copyArtifact(`%{buildroot}/%{_libexecdir}`, k, &le)
}
}

configKeys := dalec.SortMapKeys(w.Spec.Artifacts.ConfigFiles)
for _, c := range configKeys {
cfg := w.Spec.Artifacts.ConfigFiles[c]
Expand Down Expand Up @@ -599,6 +607,16 @@ func (w *specWrapper) Files() fmt.Stringer {
}
}

if w.Spec.Artifacts.Libexec != nil {
dataKeys := dalec.SortMapKeys(w.Spec.Artifacts.Libexec)
for _, k := range dataKeys {
le := w.Spec.Artifacts.Libexec[k]
targetDir := filepath.Join(`%{_libexecdir}`, le.SubPath)
fullPath := filepath.Join(targetDir, le.ResolveName(k))
fmt.Fprintln(b, fullPath)
}
}

configKeys := dalec.SortMapKeys(w.Spec.Artifacts.ConfigFiles)
for _, c := range configKeys {
cfg := w.Spec.Artifacts.ConfigFiles[c]
Expand Down
104 changes: 104 additions & 0 deletions test/azlinux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,110 @@ Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/boot
})
})

t.Run("test libexec file installation", func(t *testing.T) {
t.Parallel()
spec := &dalec.Spec{
Name: "libexec-test",
Version: "0.0.1",
Revision: "1",
License: "MIT",
Website: "https://github.com/azure/dalec",
Vendor: "Dalec",
Packager: "Dalec",
Description: "Should install specified data files",
Sources: map[string]dalec.Source{
"no_name_no_subpath": {
Inline: &dalec.SourceInline{
File: &dalec.SourceInlineFile{
Contents: "#!/usr/bin/env bash\necho hello world",
Permissions: 0o755,
},
},
},
"name_only": {
Inline: &dalec.SourceInline{
File: &dalec.SourceInlineFile{
Contents: "#!/usr/bin/env bash\necho hello world",
Permissions: 0o755,
},
},
},
"name_and_subpath": {
Inline: &dalec.SourceInline{
File: &dalec.SourceInlineFile{
Contents: "#!/usr/bin/env bash\necho hello world",
Permissions: 0o755,
},
},
},
"subpath_only": {
Inline: &dalec.SourceInline{
File: &dalec.SourceInlineFile{
Contents: "#!/usr/bin/env bash\necho hello world",
Permissions: 0o755,
},
},
},
"nested_subpath": {
Inline: &dalec.SourceInline{
File: &dalec.SourceInlineFile{
Contents: "#!/usr/bin/env bash\necho hello world",
Permissions: 0o755,
},
},
},
},
Build: dalec.ArtifactBuild{},
Artifacts: dalec.Artifacts{
Binaries: map[string]dalec.ArtifactConfig{
"no_name_no_subpath": {},
},
Libexec: map[string]dalec.ArtifactConfig{
"no_name_no_subpath": {},
"name_only": {
Name: "this_is_the_name_only",
},
"name_and_subpath": {
SubPath: "subpath",
Name: "custom_name",
},
"subpath_only": dalec.ArtifactConfig{
SubPath: "custom",
},
"nested_subpath": dalec.ArtifactConfig{
SubPath: "libexec-test/abcdefg",
},
},
},
}

testEnv.RunTest(ctx, t, func(ctx context.Context, client gwclient.Client) {
req := newSolveRequest(withBuildTarget(testConfig.Target.Container), withSpec(ctx, t, spec))
res := solveT(ctx, t, client, req)

ref, err := res.SingleRef()
if err != nil {
t.Fatal(err)
}

if err := validatePathAndPermissions(ctx, ref, "/usr/libexec/no_name_no_subpath", 0o755); err != nil {
t.Fatal(err)
}
if err := validatePathAndPermissions(ctx, ref, "/usr/libexec/this_is_the_name_only", 0o755); err != nil {
t.Fatal(err)
}
if err := validatePathAndPermissions(ctx, ref, "/usr/libexec/subpath/custom_name", 0o755); err != nil {
t.Fatal(err)
}
if err := validatePathAndPermissions(ctx, ref, "/usr/libexec/custom/subpath_only", 0o755); err != nil {
t.Fatal(err)
}
if err := validatePathAndPermissions(ctx, ref, "/usr/libexec/libexec-test/abcdefg/nested_subpath", 0o755); err != nil {
t.Fatal(err)
}
})
})

t.Run("test config files handled", func(t *testing.T) {
t.Parallel()
spec := &dalec.Spec{
Expand Down
26 changes: 26 additions & 0 deletions website/docs/artifacts.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,32 @@ artifacts:
You may use a trailing wildcard to specify multiple binaries in a directory,
though behavior may differ between different OS's/distros.
### Libexec
Libexec files are additional executable files that may be executed by one of
the main package executables. On Linux these would typically get installed into
`/usr/libexec/` or `/usr/libexec/<main-executable-name>`.

Files under libexec are a mapping of file path to [artifact configuration](#artifact-configuration).
If `subpath` is not supplied, the artifact will be installed in `/usr/libexec`
directly. The file path is the path to a file that must be available after the
build section has finished. This path is relative to the working directory of
the build phase *before* any directory changes are made.

Example:

```yaml
name: my_package
artifacts:
# the following config will install my_bin at /usr/libexec/my package/my_bin
libexec:
src/my_bin:
```

You may use a trailing wildcard to specify multiple binaries in a directory,
though behavior may differ between different OS's/distros.

### Manpages

Manpages is short for manual pages.
Expand Down

0 comments on commit eb8c95c

Please sign in to comment.