Showing 1,065 changed files with 136,860 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ AllCops:
- spec/*
- spec/acceptance/*
- rakelib/*
- plans/*.pp
- tasks/*
inherit_from: ".rubocop_todo.yml"
Metrics/LineLength:
Description: People have wide screens, use them.
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Version 3.4.0

Add Puppet Bolt tasks to interact with the Kubernetes API

# Version 3.3.0

Moves env variable to init.pp
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* [Parameters](#parameters)
4. [Limitations - OS compatibility, etc.](#limitations)
5. [Development - Guide for contributing to the module](#development)
6. [Examples - Puppet Bolt task examples](#examples)

## Description

Expand Down Expand Up @@ -644,3 +645,7 @@ Docker is the supported container runtime for this module.
## Development

If you would like to contribute to this module, please follow the rules in the [CONTRIBUTING.md](https://github.com/puppetlabs/puppetlabs-kubernetes/blob/master/CONTRIBUTING.md).

## Examples

In the examples folder you will find a [bash script](https://github.com/puppetlabs/puppetlabs-kubernetes/blob/master/examples/task_examples.sh) containg a few sample Puppet Bolt commands for the usage of the tasks. The example script is intended to be used with a Kubernetes API that requires the token authentication header, but the token parameter is optional by default.
38 changes: 38 additions & 0 deletions examples/assets/nginx_deployment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"apiVersion": "extensions/v1beta1",
"kind": "Deployment",
"metadata": {
"name": "nginx-deployment",
"labels": {
"app": "nginxtest"
}
},
"spec": {
"replicas": 3,
"selector": {
"matchLabels": {
"app": "nginxtest"
}
},
"template": {
"metadata": {
"labels": {
"app": "nginxtest"
}
},
"spec": {
"containers": [
{
"name": "nginxtest",
"image": "nginx:alpine",
"ports": [
{
"containerPort": 80
}
]
}
]
}
}
}
}
22 changes: 22 additions & 0 deletions examples/assets/nginx_deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginxtest
spec:
replicas: 3
selector:
matchLabels:
app: nginxtest
template:
metadata:
labels:
app: nginxtest
spec:
containers:
- name: nginxtest
image: nginx:alpine
ports:
- containerPort: 80
11 changes: 11 additions & 0 deletions examples/assets/nginx_service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
kind: Service
apiVersion: v1
metadata:
name: nginxtestservice
spec:
selector:
app: nginxtest
type: ClusterIP
ports:
- protocol: TCP
port: 80
19 changes: 19 additions & 0 deletions examples/task_examples.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash

endpoint=${KUBE_API:-https://localhost:8080}
token=$KUBE_TOKEN

#List namespaces
bolt task run --nodes localhost k8s::swagger_k8s_list_core_v1_namespace kube_api=$endpoint token=$token

#Create a pod
#bolt task run --nodes localhost k8s::swagger_k8s_create_core_v1_namespaced_pod kube_api=$endpoint apiversion="v1" kind="Pod" spec="{ 'containers' => [{'name'=>'nginx'; 'image'=>'nginx'; 'ports' => [{'containerPort'=>80}]}] }" metadata="{ 'name' => 'nginx-test' }" namespace="default" token=$token

#Create a deployment
#bolt task run --nodes localhost k8s::swagger_k8s_create_extensions_v1beta1_namespaced_deployment kube_api=$endpoint body="$PWD/assets/nginx_deployment.json" namespace="default" token=$token

#Create a service
#bolt task run --nodes localhost k8s::swagger_k8s_create_core_v1_namespaced_service kube_api=$endpoint token=$token body="$PWD/assets/nginx_service.yaml" namespace="default"

#Delete a service
#bolt task run --nodes localhost k8s::swagger_k8s_delete_core_v1_namespaced_service kube_api=$endpoint token=$token metadata="{'name'=>'nginxtestservice'}" namespace="default" name="nginxtestservice"
2 changes: 1 addition & 1 deletion metadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "puppetlabs-kubernetes",
"version": "3.3.0",
"version": "3.4.0",
"author": "Puppet",
"summary": "The module installs and configures a Kubernetes cluster",
"license": "Apache-2.0",
Expand Down
19 changes: 19 additions & 0 deletions plans/deploy.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# This plan is meant to create a deployment and a service on Kubernetes
# To run this plan you will have to enable the anonymous user access to you Kubernetes environment or add the necessary authentication parameters (token and ca_file for each task)
# Command to system:anonymous rights: kubectl create clusterrolebinding cluster-system-anonymous --clusterrole=cluster-admin --user=system:anonymous
plan k8s::deploy(
String[1] $name,
String[1] $namespace,
Integer $replicas,
String[1] $image,
Integer $container_port,
String[1] $endpoint,
) {
$responses=run_task('k8s::swagger_k8s_create_extensions_v1beta1_namespaced_deployment', 'localhost', namespace => $namespace, kube_api => $endpoint, apiversion=> 'extensions/v1beta1', kind => 'Deployment', metadata => "{'name' => '${name}'; 'labels' => { 'app' => '${name}' } }", spec => "{ 'replicas' => ${replicas}; 'selector' => {'matchLabels' => {'app' => '${name}' }}; 'template' => { 'metadata' => {'labels'=>{ 'app' => '${name}' }} ; 'spec' => { 'containers' => [ { 'name' => '${name}'; 'image' => '${image}'; 'ports' => [ {'containerPort' => ${container_port} }] } ] } } }")

notice($responses.first)

$service= run_task('k8s::swagger_k8s_create_core_v1_namespaced_service','localhost', namespace => $namespace, kube_api => $endpoint, apiversion => 'v1', kind => 'Service', metadata => "{'name' => '${name}'}", spec => "{'selector'=>{'app'=>'${name}'};'type'=>'ClusterIP';'ports'=>[{'protocol'=>'TCP';'port'=>${container_port}}]}" )

notice($service)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"description": "create a MutatingWebhookConfiguration",
"input_method": "stdin",
"parameters":{
"kube_api":{
"description": "Kubernetes API endpoint",
"type": "String"
},
"ca_file":{
"description": "Certificate file path",
"type": "Optional[String[1]]"
},
"token":{
"description": "Authentication token obtained from 'kubectl describe secret <secret_name>'",
"type": "Optional[String[1]]"
},





"dry_run":{
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"type": "Optional[String[1]]"
}
,

"field_manager":{
"description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.",
"type": "Optional[String[1]]"
}
,

"pretty":{
"description": "If 'true', then the output is pretty printed.",
"type": "Optional[String[1]]"
}
,

"apiversion":{
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
"type": "Optional[String[1]]"
}
,

"kind":{
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
"type": "Optional[String[1]]"
}
,

"metadata":{
"description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata.",
"type": "Optional[String[1]]"
}
,

"webhooks":{
"description": "Webhooks is a list of webhooks and the affected resources and operations.",
"type": "Optional[String[1]]"
}
,

"body":{
"description": "",
"type": "Optional[String[1]]"
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#!/opt/puppetlabs/puppet/bin/ruby

require 'json'
require 'puppet'
require 'openssl'

def create_admissionregistration_v1beta1_mutating_webhook_configuration(*args)
header_params = {}

params=args[0][1..-1].split(',')

arg_hash={}
params.each { |param|
mapValues= param.split(':',2)
if mapValues[1].include?(';')
mapValues[1].gsub! ';',','
end
arg_hash[mapValues[0][1..-2]]=mapValues[1][1..-2]
}

# Remove task name from arguments - should contain all necessary parameters for URI
arg_hash.delete('_task')
operation_verb = 'Post'

query_params, body_params, path_params = format_params(arg_hash)

uri_string = "#{arg_hash['kube_api']}/apis/admissionregistration.k8s.io/v1beta1/mutatingwebhookconfigurations" % path_params

if query_params
uri_string = uri_string + '?' + to_query(query_params)
end

header_params['Content-Type'] = 'application/json' # first of #{parent_consumes}

if arg_hash['token']
header_params['Authentication'] = 'Bearer ' + arg_hash['token']
end

uri = URI(uri_string)

verify_mode= OpenSSL::SSL::VERIFY_NONE
if arg_hash['ca_file']
verify_mode=OpenSSL::SSL::VERIFY_PEER
end

Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https', verify_mode: verify_mode, ca_file: arg_hash['ca_file']) do |http|
if operation_verb == 'Get'
req = Net::HTTP::Get.new(uri)
elsif operation_verb == 'Put'
req = Net::HTTP::Put.new(uri)
elsif operation_verb == 'Delete'
req = Net::HTTP::Delete.new(uri)
elsif operation_verb == 'Post'
req = Net::HTTP::Post.new(uri)
end

header_params.each { |x, v| req[x] = v } unless header_params.empty?

unless body_params.empty?
if body_params.key?('file_content')
req.body = body_params['file_content']
else
req.body = body_params.to_json
end
end

Puppet.debug("URI is (#{operation_verb}) #{uri} headers are #{header_params}")
response = http.request req # Net::HTTPResponse object
Puppet.debug("response code is #{response.code} and body is #{response.body}")
success = response.is_a? Net::HTTPSuccess
Puppet.debug("Called (#{operation_verb}) endpoint at #{uri}, success was #{success}")
response
end
end

def to_query(hash)
if hash
return_value = hash.map { |x, v| "#{x}=#{v}" }.reduce { |x, v| "#{x}&#{v}" }
if !return_value.nil?
return return_value
end
end
return ''
end

def op_param(name, inquery, paramalias, namesnake)
{ :name => name, :location => inquery, :paramalias => paramalias, :namesnake => namesnake }
end

def format_params(key_values)
query_params = {}
body_params = {}
path_params = {}

key_values.each { | key, value |
if value.include?("=>")
Puppet.debug("Running hash from string on #{value}")
value.gsub!("=>",":")
value.gsub!("'","\"")
key_values[key] = JSON.parse(value)
Puppet.debug("Obtained hash #{key_values[key].inspect}")
end
}


if key_values.key?('body')
if File.file?(key_values['body'])
if key_values['body'].include?('json')
body_params['file_content'] = File.read(key_values['body'])
else
body_params['file_content'] =JSON.pretty_generate(YAML.load_file(key_values['body']))
end
end
end

op_params = [
op_param('apiversion', 'body', 'api_version', 'apiversion'),
op_param('body', 'body', 'body', 'body'),
op_param('dryRun', 'query', 'dry_run', 'dry_run'),
op_param('fieldManager', 'query', 'field_manager', 'field_manager'),
op_param('kind', 'body', 'kind', 'kind'),
op_param('metadata', 'body', 'metadata', 'metadata'),
op_param('pretty', 'query', 'pretty', 'pretty'),
op_param('webhooks', 'body', 'webhooks', 'webhooks'),
]
op_params.each do |i|
location = i[:location]
name = i[:name]
paramalias = i[:paramalias]
name_snake = i[:namesnake]
if location == 'query'
query_params[name] = key_values[name_snake] unless key_values[name_snake].nil?
query_params[name] = ENV["azure__#{name_snake}"] unless ENV["<no value>_#{name_snake}"].nil?
elsif location == 'body'
body_params[name] = key_values[name_snake] unless key_values[name_snake].nil?
body_params[name] = ENV["azure_#{name_snake}"] unless ENV["<no value>_#{name_snake}"].nil?
else
path_params[name_snake.to_sym] = key_values[name_snake] unless key_values[name_snake].nil?
path_params[name_snake.to_sym] = ENV["azure__#{name_snake}"] unless ENV["<no value>_#{name_snake}"].nil?
end
end

return query_params,body_params,path_params
end

def task
# Get operation parameters from an input JSON
params = STDIN.read
result = create_admissionregistration_v1beta1_mutating_webhook_configuration(params)
if result.is_a? Net::HTTPSuccess
puts result.body
else
raise result.body
end
rescue StandardError => e
result = {}
result[:_error] = {
msg: e.message,
kind: 'puppetlabs-kubernetes/error',
details: { class: e.class.to_s },
}
puts result
exit 1
end

task
Loading