API-Docker

 view release on metacpan or  search on metacpan

lib/API/Docker/API/Secrets.pm  view on Meta::CPAN

  croak __PACKAGE__ . '->update secret ID or name required'
    unless defined $id && length $id;
  croak __PACKAGE__ . '->update requires the current version as its second '
    . 'argument: the Version.Index from inspect($id), which the daemon uses '
    . 'as an optimistic-concurrency token and will not accept the update '
    . 'without'
    unless defined $version;
  croak __PACKAGE__ . '->update version must be the numeric Version.Index '
    . "from inspect(\$id), got '$version'"
    unless $version =~ /\A[0-9]+\z/;
  $spec{Data} = $self->_encode_data('update', $spec{Data})
    if defined $spec{Data};
  return $self->client->post("/secrets/$id/update", \%spec,
    params => { version => $version });
}


sub remove {
  my ($self, $id) = @_;
  croak __PACKAGE__ . '->remove secret ID or name required'
    unless defined $id && length $id;
  return $self->client->delete_request("/secrets/$id",
    %{ $self->_request_options },
  );
}



1;

__END__

=pod

=encoding UTF-8

=head1 NAME

API::Docker::API::Secrets - Docker Engine Secrets API

=head1 VERSION

version 0.004

=head1 SYNOPSIS

    my $docker = API::Docker->new;

    # List secrets
    my $secrets = $docker->secrets->list;

    # Create a secret -- Data is RAW BYTES, this class base64-encodes it
    my $created = $docker->secrets->create(
        Name   => 'my-secret',
        Data   => "hunter2\n",
        Labels => { env => 'prod' },
    );

    # Inspect a secret -- an API::Docker::Type::Secret
    my $secret = $docker->secrets->inspect($created->{ID});
    say $secret->spec->name;

    # Update: the version comes from the inspect above, and is mandatory
    my %spec = %{ $secret->spec->TO_JSON };
    $spec{Labels} = { env => 'staging' };
    $secret->update(%spec);

    # Remove
    $docker->secrets->remove($created->{ID});

=head1 DESCRIPTION

This module provides methods for managing Docker secrets (C</secrets>):
listing, creation, inspection, update and removal.

Accessed via C<< $docker->secrets >>, or through
L<API::Docker::Role::Using/using> for a run of calls that needs its own
transport bound: C<< $docker->secrets->using(read_timeout => 5) >>.

L</list> and L</inspect> return L<API::Docker::Type::Secret> objects carrying
the convenience methods of L<API::Docker::Role::Entity::Secret>. It is B<one>
class for both, where containers and images have two: the swagger answers
C<GET /secrets> with an array of the C<Secret> definition and
C<GET /secrets/{id}> with that same definition. Field names are the
swagger's own spelling in snake_case -- C<ID> is C<< ->id >>, C<CreatedAt> is
C<< ->created_at >> -- and the nested ones are generated classes rather than
the raw HashRefs the old entity kept: C<< $secret->spec >> is an
L<API::Docker::Type::SecretSpec> and C<< $secret->version >> an
L<API::Docker::Type::ObjectVersion>, whose C<< ->index >> is what
C<version_index> reaches.

The value of a secret is write-only. L</list> and L</inspect> return the
metadata -- C<< ->id >>, C<< ->spec >>, C<< ->created_at >>,
C<< ->version >> -- and never the payload; the engine hands that out to
containers, not over this API. If you need to read the value back, this is
the wrong storage: use L<API::Docker::API::Configs>, whose entity offers a
C<decoded_data> because the daemon actually sends one.

=head2 Data is raw bytes; this class does the base64

The wire field C<Data> carries base64. B<This class encodes it for you.> Pass
L</create> raw bytes and they go out encoded; do not pre-encode, or the daemon
faithfully stores your base64 text as the secret.

That division of labour is not a matter of taste, because the daemon does not
validate what it decodes. Measured against Podman 5.4.2: a C<Data> of the
plain text C<"hello there!"> was accepted with B<HTTP 200> and stored three
bytes of garbage -- Go's decoder consumed the leading C<"hell">, stopped at
the space, and reported nothing. A caller left to encode their own payload can
therefore corrupt a secret and be told it succeeded. Doing it here removes
that failure mode from the caller entirely.

The alphabet is B<standard> base64 with padding (C<+> and C</>), not the
URL-safe one, and unwrapped. The Engine API reference calls the field
"base64-url-safe-encoded"; that is measurably not what the engine accepts. The
same four bytes sent as C<-v_--w==> were rejected with B<500>
C<"secret data must be larger than 0 and less than 512000 bytes"> -- the
URL-safe alphabet decoded to nothing -- where C<+v/++w==> was stored correctly.

C<Data> must be a byte string. A string holding characters above C<U+00FF>
croaks here rather than reaching L<MIME::Base64>, which would die with a bare



( run in 0.528 second using v1.01-cache-2.11-cpan-a49fcb8fa48 )