EV-Etcd

 view release on metacpan or  search on metacpan

Makefile.PL  view on Meta::CPAN

my $grpc_cflags = `pkg-config --cflags grpc 2>/dev/null` || '';
my $grpc_libs = `pkg-config --libs grpc 2>/dev/null` || '-lgrpc';
chomp($grpc_cflags, $grpc_libs);

# Try pkg-config for libprotobuf-c
my $protobuf_c_cflags = `pkg-config --cflags libprotobuf-c 2>/dev/null` || '';
my $protobuf_c_libs = `pkg-config --libs libprotobuf-c 2>/dev/null` || '-lprotobuf-c';
chomp($protobuf_c_cflags, $protobuf_c_libs);

# Detect gRPC API version for channel creation compatibility
# - gRPC >= ~1.42: grpc_insecure_credentials_create() + grpc_channel_create()
# - gRPC <  ~1.42: grpc_insecure_channel_create()
# The new API header moved from grpc_security.h to credentials.h in ~1.65
use File::Temp qw(tempdir);
my $grpc_api_defines = '';
my $tmpdir = tempdir(CLEANUP => 1);

sub try_compile {
    my ($cflags, $code) = @_;
    open my $fh, '>', "$tmpdir/test.c" or return 0;
    print $fh $code;
    close $fh;
    my $cc = $Config{cc} || 'cc';

Makefile.PL  view on Meta::CPAN


Install them with:
  Debian/Ubuntu:  apt install libgrpc-dev libgrpc++-dev libprotobuf-c-dev
  macOS:          brew install grpc protobuf-c
  FreeBSD:        pkg install grpc protobuf-c
*******************************************
MSG
    exit 0;  # exit 0 = NA on CPAN Testers (not a test failure)
}

# Check for grpc/credentials.h (gRPC >= ~1.65)
if (try_compile($all_cflags, <<'END')) {
#include <grpc/credentials.h>
int main(void) { return 0; }
END
    $grpc_api_defines .= ' -DHAVE_GRPC_CREDENTIALS_H';
    print "Detected <grpc/credentials.h>\n";
}

# Check for new credentials-based channel API
my $creds_test_header = ($grpc_api_defines =~ /HAVE_GRPC_CREDENTIALS_H/)
    ? '#include <grpc/credentials.h>' : '#include <grpc/grpc_security.h>';
if (try_compile("$all_cflags $grpc_api_defines", <<"END")) {
#include <grpc/grpc.h>
$creds_test_header
void test(void) { grpc_channel_credentials *c = grpc_insecure_credentials_create(); (void)c; }
int main(void) { return 0; }
END
    $grpc_api_defines .= ' -DHAVE_GRPC_NEW_CHANNEL_API';
    print "Detected grpc_insecure_credentials_create (new channel API)\n";
} else {
    print "Using legacy grpc_insecure_channel_create\n";
}

WriteMakefile(
    NAME              => 'EV::Etcd',
    VERSION_FROM      => 'lib/EV/Etcd.pm',
    ABSTRACT_FROM     => 'lib/EV/Etcd.pm',
    AUTHOR            => 'vividsnow',
    LICENSE           => 'perl_5',

etcd_common.h  view on Meta::CPAN


/* Threading support for hybrid gRPC/EV approach */
#include <pthread.h>

/* Reconnect backoff: 0.5s * attempt (capped at 5s) */
#define RECONNECT_BACKOFF_SECONDS(attempt) \
    ((attempt) * 0.5 > 5.0 ? 5.0 : (attempt) * 0.5)

#include <grpc/grpc.h>
#ifdef HAVE_GRPC_CREDENTIALS_H
#include <grpc/credentials.h>
#else
#include <grpc/grpc_security.h>
#endif
#include <grpc/byte_buffer.h>
#include <grpc/byte_buffer_reader.h>

/*
 * gRPC channel creation compatibility.
 * - New API (>= ~1.42): grpc_insecure_credentials_create + grpc_channel_create
 * - Old API (< ~1.42):  grpc_insecure_channel_create
 */
static inline grpc_channel *
etcd_create_insecure_channel(const char *target, const grpc_channel_args *args) {
#ifdef HAVE_GRPC_NEW_CHANNEL_API
    grpc_channel_credentials *creds = grpc_insecure_credentials_create();
    grpc_channel *channel = grpc_channel_create(target, creds, args);
    grpc_channel_credentials_release(creds);
    return channel;
#else
    return grpc_insecure_channel_create(target, args, NULL);
#endif
}

#include "kv.pb-c.h"
#include "rpc.pb-c.h"
#include "lock.pb-c.h"
#include "election.pb-c.h"

etcd_common.h  view on Meta::CPAN

        } \
    } while (0)

#define VALIDATE_VALUE_SIZE(value_len) \
    do { \
        if ((value_len) > ETCD_MAX_VALUE_SIZE) { \
            croak("value too large: %zu bytes (max %d)", (size_t)(value_len), ETCD_MAX_VALUE_SIZE); \
        } \
    } while (0)

/* Auth input limits - prevent DoS via oversized credentials */
#define ETCD_MAX_USERNAME_SIZE  256   /* Reasonable username limit */
#define ETCD_MAX_PASSWORD_SIZE  4096  /* Reasonable password limit */

#define VALIDATE_USERNAME_SIZE(len) \
    do { \
        if ((len) > ETCD_MAX_USERNAME_SIZE) { \
            croak("username too large: %zu bytes (max %d)", (size_t)(len), ETCD_MAX_USERNAME_SIZE); \
        } \
    } while (0)

lib/EV/Etcd.pm  view on Meta::CPAN


=back

B<Note>: You can also release a lock by revoking its associated lease with
C<lease_revoke>. This is useful if you want to release all resources
associated with a lease at once.

=head1 AUTHENTICATION SERVICE

EV::Etcd provides full support for etcd's authentication and authorization
system. Authentication uses username/password credentials, and authorization
is based on roles with key-range permissions.

=head2 authenticate

    $client->authenticate($username, $password, $callback);

Authenticate with etcd using username and password. On success, the client
automatically stores the auth token and uses it for subsequent requests.

Arguments:

t/auth.t  view on Meta::CPAN

{
    my $wrong_err;
    $client->authenticate($test_user, 'wrong-password', sub {
        my ($resp, $err) = @_;
        $wrong_err = $err;
        EV::break;
    });
    my $t_wrong = EV::timer(5, 0, sub { fail('timeout'); EV::break });
    EV::run;

    # Should return an error (either auth not enabled, or invalid credentials)
    ok($wrong_err, 'authenticate with wrong password returns error');
    diag("Wrong password error: " . ($wrong_err->{message} // $wrong_err));
}

# Test 21-22: user_change_password
{
    my $change_result;
    my $change_err;
    $client->user_change_password($test_user, 'new-password-456', sub {
        my ($resp, $err) = @_;



( run in 1.793 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )