package Net::Kubernetes;
$Net::Kubernetes::VERSION = '1.08';
# ABSTRACT: An object oriented interface to the REST API's provided by kubernetes

use Moose;
require Net::Kubernetes::Namespace;
require LWP::UserAgent;
require HTTP::Request;
require URI;
require Throwable::Error;
use MIME::Base64;
require Net::Kubernetes::Exception;


with 'Net::Kubernetes::Role::APIAccess';
with 'Net::Kubernetes::Role::ResourceFetcher';
with 'Net::Kubernetes::Role::ResourceCatalog';


has 'default_namespace' => (
    is       => 'rw',
    isa      => 'Net::Kubernetes::Namespace',
    required => 0,
    lazy     => 1,
    handles  => [
        qw(
            build_secret create create_from_file get_deployment get_pod
            get_rc get_replica_set get_replication_controller get_rs
            get_secret get_service get_role get_role_binding get_service_account
            list_deployments list_endpoints
            list_events list_pods list_rc list_replica_sets
            list_replication_controllers list_rs list_secrets list_services
            list_roles list_role_bindings list_service_accounts
            )
    ],
    builder => '_get_default_namespace',
);

sub get_namespace {
    my ($self, $namespace) = @_;
    if (!defined $namespace || !length $namespace) {
        Throwable::Error->throw(message => '$namespace cannot be null');
    }
    my $namespace_path = $self->base_path . "/namespaces/$namespace";
    my $res = $self->ua->request($self->create_request(GET => $self->url . $namespace_path));
    if ($res->is_success) {
        my $ns = $self->json->decode($res->content);

        # Somewhere between Kubernetes 1.2 and 1.5, the self link for namespaces broke. So for now, we can't trust them.
        # to populate the base_path. A bug report indicates that this bug is fixed in 1.7.
        # https://github.com/kubernetes/kubernetes/issues/48321
        my (%create_args) = (
            url             => $self->url,
            base_path       => $namespace_path,
            server_version  => $self->server_version,
            api_version     => $self->api_version,
            namespace       => $namespace,
            _namespace_data => $ns
        );
        $create_args{username}      = $self->username      if (defined $self->username);
        $create_args{password}      = $self->password      if (defined $self->password);
        $create_args{token}         = $self->token         if (defined $self->token);
        $create_args{ssl_cert_file} = $self->ssl_cert_file if (defined $self->ssl_cert_file);
        $create_args{ssl_key_file}  = $self->ssl_key_file  if (defined $self->ssl_key_file);
        $create_args{ssl_ca_file}   = $self->ssl_ca_file   if (defined $self->ssl_ca_file);
        $create_args{ssl_verify}    = $self->ssl_verify;
        return Net::Kubernetes::Namespace->new(%create_args);
    }
    else {
        Net::Kubernetes::Exception->throw(
            code    => $res->code,
            message => "Error getting namespace $namespace:\n" . $res->message
        );
    }
}

sub create_namespace {
    my ($self, $namespace) = @_;

    if (!defined $namespace || !length $namespace) {
        Throwable::Error->throw(message => '$namespace cannot be null');
    }

    my $namespace_path = $self->get_resource_path('namespace');
    my $res = $self->ua->request($self->create_request(
        POST => $namespace_path, 
        undef, 
        $self->json->encode({ 
            metadata => {
                name => $namespace
            }
        }),
    ));

    if ($res->is_success) {
        my $ns = $self->json->decode($res->content);

        # Somewhere between Kubernetes 1.2 and 1.5, the self link for namespaces broke. So for now, we can't trust them.
        # to populate the base_path. A bug report indicates that this bug is fixed in 1.7.
        # https://github.com/kubernetes/kubernetes/issues/48321
        $namespace_path .= "/$namespace";
	
        my (%create_args) = (
            url             => $self->url,
            base_path       => $namespace_path,
            server_version  => $self->server_version,
            api_version     => $self->api_version,
            namespace       => $namespace,
            _namespace_data => $ns
        );
        $create_args{username}      = $self->username      if (defined $self->username);
        $create_args{password}      = $self->password      if (defined $self->password);
        $create_args{token}         = $self->token         if (defined $self->token);
        $create_args{ssl_cert_file} = $self->ssl_cert_file if (defined $self->ssl_cert_file);
        $create_args{ssl_key_file}  = $self->ssl_key_file  if (defined $self->ssl_key_file);
        $create_args{ssl_ca_file}   = $self->ssl_ca_file   if (defined $self->ssl_ca_file);
        $create_args{ssl_verify}    = $self->ssl_verify;
        return Net::Kubernetes::Namespace->new(%create_args);
    }
    else {
        Net::Kubernetes::Exception->throw(
            code    => $res->code,
            message => "Error creating namespace $namespace:\n" . $res->message
        );
    }

}


sub list_nodes {
    my $self = shift;
    my (%options);
    if (ref($_[0])) {
        %options = %{$_[0]};
    }
    else {
        %options = @_;
    }

    my $uri = URI->new($self->path . '/nodes');
    my (%form) = ();
    $form{labelSelector} = $self->build_selector_from_hash($options{labels}) if (exists $options{labels});
    $form{fieldSelector} = $self->build_selector_from_hash($options{fields}) if (exists $options{fields});
    $uri->query_form(%form);

    my $res = $self->ua->request($self->create_request(GET => $uri));
    if ($res->is_success) {
        my $node_list = $self->json->decode($res->content);
        my (@nodes) = ();
        foreach my $node (@{$node_list->{items}}) {
            $node->{apiVersion} = $node_list->{apiVersion};
            push @nodes, $self->create_resource_object($node, 'Node');
        }
        return wantarray ? @nodes : \@nodes;
    }
    else {
        Net::Kubernetes::Exception->throw(
            code    => $res->code,
            message => $res->message
        );
    }
}

sub get_node {
    my ($self, $name) = @_;
    Net::Kubernetes::Exception->throw(message => "Missing required parameter 'name'") if (!defined $name || !length $name);
    return $self->get_resource_by_name($name, 'node');
}

sub _get_default_namespace {
    my ($self) = @_;
    return $self->get_namespace('default');
}

# SEEALSO: Net::Kubernetes::Namespace, Net::Kubernetes::Resource

return 42;

__END__

=pod

=encoding UTF-8

=head1 NAME

Net::Kubernetes - An object oriented interface to the REST API's provided by kubernetes

=head1 VERSION

version 1.08

=head1 SYNOPSIS

  my $kube = Net::Kubernetes->new(url=>'http://127.0.0.1:8080', username=>'dave', password=>'davespassword');

  # List methods will return either a list or an array reference.
  my $pod_list = $kube->list_pods();
  my @pods     = $kube->list_pods();

  my @rcs  = $kube->list_replication_controllers();
  my @rcs2 = $kube->list_rc();

  my @deployments  = $kube->list_deployments();

  my @replica_sets  = $keyb->list_replica_sets();
  my @replica_sets2 = $keyb->list_rs();

  my $nginx_pod = $kube->create_from_file('kubernetes/examples/pod.yaml');

  my $ns = $kube->get_namespace('default');

  # Namespaces contain all the list methods above as well.
  my $services = $ns->list_services;

  my $pod = $ns->get_pod('my-pod');

  $pod->delete;

  my $other_pod = $ns->create_from_file('./my-pod.yaml');

=begin html

<h2>Build Status</h2>

<img src="https://travis-ci.org/perljedi/net-kubernetes.svg?branch=release-0.21" />

=end html

=head2 new - Create a new $kube object

All parameters are optional and have some basic default values (where appropriate).

=over 1

=item url ['http://localhost:8080']

The base url for the kubernetes. This should include the protocal (http or https) but not "/api/v1beta3" (see base_path).

=item base_path ['/api/v1beta3']

The entry point for api calls, this may be used to set the api version with which to interact.

=item username

Username to use with basic authentication. If either username or password are not provided, basic authentication will not
be used.

=item password

Password to use with basic authentication. If either username or password are not provided, basic authentication will not
be used.

=item token

An authentication token to be used to access the apiserver.  This may be provided as a plain string, a path to a file
from which to read the token (like /var/run/secrets/kubernetes.io/serviceaccount/token from within a pod), or a reference
to a file handle (from which to read the token).

=item ssl_cert_file, ssl_key_file, ssl_ca_file

This there options passed into new will cause Net::Kubernetes in inlcude SSL client certs to requests to the kuberernetes
API server for authentication.  There are basically just a passthrough to the underlying LWP::UserAgent used to handle the
api requests.

=item server_version

This module attempts to make some decisions on how it talks to kubernetes based upon the version of kubernetes it connects to.
If this is not passed in, the first call to kubernetes will attempt to retrieve server version information from the server.

=back

=head2 get_namespace("myNamespace");

This method returns a "Namespace" object on which many methods can be called implicitly
limited to the specified namespace.

=head2 get_pod('my-pod-name')

Delegates automatically to L<Net::Kubernetes::Namespace> via $self->get_namespace('default')

=head2 get_repllcation_controller('my-rc-name') (aliased as $ns->get_rc('my-rc-name'))

Delegates automatically to L<Net::Kubernetes::Namespace> via $self->get_namespace('default')

=head2 get_service('my-servce-name')

Delegates automatically to L<Net::Kubernetes::Namespace> via $self->get_namespace('default')

=head2 get_secret('my-secret-name')

Delegates automatically to L<Net::Kubernetes::Namespace> via $self->get_namespace('default')

=head2 list_nodes([label=>{label=>value}], [fields=>{field=>value}])

returns a list of L<Net::Kubernetes::Resource::Node>s

=head1 AUTHOR

Dave Mueller <dave@perljedi.com>

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2015 by Liquid Web Inc.

This is free software, licensed under:

  The MIT (X11) License

=head1 SEE ALSO

Please see those modules/websites for more information related to this module.

=over 4

=item *

L<Net::Kubernetes::Namespace|Net::Kubernetes::Namespace>

=item *

L<Net::Kubernetes::Resource|Net::Kubernetes::Resource>

=back

=head1 CONSUMES

=over 4

=item * L<Net::Kubernetes::Role::APIAccess>

=item * L<Net::Kubernetes::Role::ResourceCatalog>

=item * L<Net::Kubernetes::Role::ResourceFactory>

=item * L<Net::Kubernetes::Role::ResourceFetcher>

=back

=head1 CONTRIBUTORS

=for stopwords Chris Reinhardt Christopher Pruden Dave Mueller Kevin Johnson

=over 4

=item *

Chris Reinhardt <creinhardt@liquidweb.com>

=item *

Christopher Pruden <cdpruden@liquidweb.com>

=item *

Dave <dave@perljedi.com>

=item *

Dave Mueller <dmueller@liquidweb.com>

=item *

Kevin <kcavemanj@gmail.com>

=item *

Kevin Johnson <kcavemanj@gmail.com>

=back

=cut
