Skip to content

Std ops.mkStandardOCI compatibility problem with ops.mkOperable to wrap #318

@Pegasust

Description

@Pegasust

Fixed in #331


I have a pretty standard poetry2nix web application that I wrap on its environment derivation using hypercorn. The built OCI fails to detect the correct binary location because it relies on lib.getExe. mkOperable works as expected, though.

Symptoms

I don't have the exact error that throws from Docker engine. It should be something along the line of Docker container not being to start up because /bin/entrypoint not found.

If you have access to ls (I use Python's os.listdir because I'm too lazy to build a dev layer), You would see that /bin/entrypoint exists, but it is a broken symlink.

$ std //ops/oci/p2n-env-oci:load
docker-daemon:<image>:<hash>
$ docker run -it --rm --entrypoint /bin/sh <image>:<hash>
$ python3
import os

def follow_symlinks(path):
    if os.path.islink(path):
        link_path = os.readlink(path)
        print(f'{path} -> {link_path}')
        return follow_symlinks(link_path)
    elif os.path.exists(path):
        print(f'Path is a real file or directory: {path}')
        return path
    else:
        print(f'Path not found: {path}')
        return None

follow_symlinks('/bin/entrypoint')

One example output is

/bin/entrypoint -> /nix/store/<hash>-oci-setup-links/bin/entrypoint
/nix/store/<hash>-oci-setup-links/bin/entrypoint -> /nix/store/<hash>-operable-python3-3.10.11-env/bin/operable-python3
Path not found: /nix/store/<hash>-operable-python3-3.10.11-env/bin/operable-python3
>>> os.listdir('/nix/store/<hash>-operable-python3-3.10.11-env/bin/')
["operable-python3-3.10.11-env"]

Workaround

  p2n-env-fix = workspace_app.dependencyEnv.overrideAttrs (prev: {
    # HACK: This allows mkOperable and mkStandardOCI to resolve symlink correctly
    name = "p2n-env-fix";
    meta.mainProgram = "python3";
  });

Nix flakes and its workaround variant

# nix/dev/packages/tools.nix
{
  inputs,
  cell,
}: let
  system = inputs.nixpkgs.system;
  poetry2nix = inputs.nix-boost.pkgs.${system}.mypkgs.poetry2nix;
  workspace_root = "${inputs.self}";
  workspace_app = poetry2nix.mkPoetryApplication {
    projectDir = workspace_root;
  };
in {
  p2n-env = workspace_app.dependencyEnv;
  p2n-env-fix = workspace_app.dependencyEnv.overrideAttrs (prev: {
    # HACK: This allows mkOperable and mkStandardOCI to resolve symlink correctly
    name = "p2n-env-fix";
    meta.mainProgram = "python3";
  });
  frontend = {}; # omitted
}

# nix/ops/exe.nix
{
  inputs,
  cell,
}: let
  inherit (inputs.std.lib) ops;
  runtimeScript = ''
      hypercorn my.backend:app --bind '0.0.0.0:10140' --worker-class uvloop
  '';
  runtimeEnv = {
    DIST_LOC = "${inputs.cells.dev.packages.frontend}/dist";
  };
in {
  p2n-env-ops = ops.mkOperable {
    package = inputs.cells.dev.packages.p2n-env;
    runtimeInputs = [
      inputs.cells.dev.packages.p2n-env
      inputs.cells.dev.packages.frontend
    ];
    inherit runtimeScript runtimeEnv;
  };

  p2n-env-fix-ops = ops.mkOperable {
    package = inputs.cells.dev.packages.p2n-env-fix;
    runtimeInputs = [
      inputs.cells.dev.packages.p2n-env-fix
      inputs.cells.dev.packages.frontend
    ];
    inherit runtimeScript runtimeEnv;
  };
}

# nix/ops/oci.nix
{
  inputs,
  cell,
}: let
  inherit (inputs.std.lib) ops;
  pyproject = builtins.fromTOML (builtins.readFile "${inputs.self}/pyproject.toml");
  ws-authors = pyproject.tool.poetry.authors;
in {
  p2n-env-oci = ops.mkStandardOCI {
    name = "p2n-env-oci";
    # NOTE: swap to p2n-env-fix-ops fixes it
    operable = inputs.cells.ops.exe.p2n-env-ops;
    meta = {
      # NOTE: unfair, std doesn't allow me to give more tags :(
      # including this and `nix` and `std` tells me to remove either `meta.tags` or `tag`
      # tags = ["ops-latest" "ops-${ws-version}" "${ws-name}-${ws-version}"];
    };
    config = {
      ExposedPorts."10140/tcp" = {};
      Volumes = {
        # For secrets and env
        "/var/run" = {};
      };
      Env = [
        "ENV_PATH=/var/run/.env"
        "GNMI_CONFIG_LOC=/var/run/.config.yml"
      ];
      Labels."org.opencontainers.image.title" = "p2n-env";
      Labels."org.opencontainers.image.authors" = builtins.concatStringsSep ", " ws-authors;
    };
  };

  # Currently, there is no good documented way to publish to arbitrary repositories at once
  # NOTE: untested
  p2n-env-gl = cell.oci.p2n-env-oci.overrideAttrs {
    meta.repo = "gitlab.<domain>.com/<group>/<repo>/<image>";
  };
}

Some more insights from build artifacts

nix-repl> packages.aarch64-darwin.p2n-env.name
"python3-3.10.11-env"

nix-repl> packages.aarch64-darwin.p2n-env.meta.name
"python3-3.10.11"

nix-repl> lib.getExe packages.aarch64-darwin.p2n-env-ops
"/nix/store/<hash>-operable-python3-3.10.11-env/bin/operable-python3"

Thoughts

Requests

Metadata

Metadata

Assignees

No one assigned

    Labels

    💪 Effort: 3This issue is of medium complexity or only partly well understood🆗 Status: AcceptedThis issue has been accepted

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions