kuby-gke 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 37f0c24f6f6c990561c05d5c5478b305ccee9e7f46bf83451b6b3c555360495a
4
+ data.tar.gz: 3436d7ac77a8dce070102e215b5f6184e981ab7505da2b5036c1607fe59a04df
5
+ SHA512:
6
+ metadata.gz: 70241d2d6c6d3676d7f7c301be95ed84695495d854139bd656cf3fe77033a695d1c90d679933dfdcb84d5ac0c92487b87ae0362e92aa47ce95e083207b770d01
7
+ data.tar.gz: fc0f7de8a141c5d7cb30c61202e798dff39129d9db6969efef7a6203dbbd5f2515fd613605bad66e1b325e8a29e0ec55c8ce751cf9f2733d4b2c227f60ccc0a4
data/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ ## 0.1.0
2
+ * Birthday!
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'kuby-core'
6
+
7
+ group :development, :test do
8
+ gem 'debug'
9
+ gem 'rake'
10
+ end
11
+
12
+ group :test do
13
+ gem 'rspec', '~> 3.0'
14
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Cameron Dutro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ ## kuby-gke
2
+
3
+ Google Kubernetes Engine (GKE) provider for [Kuby](https://github.com/getkuby/kuby-core).
4
+
5
+ ## Intro
6
+
7
+ In Kuby parlance, a "provider" is an [adapter](https://en.wikipedia.org/wiki/Adapter_pattern) that enables Kuby to deploy apps to a specific cloud provider. In this case, we're talking about Google's [Cloud Platform](https://cloud.google.com/), specifically their managed Kubernetes offering, [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine) (GKE).
8
+
9
+ All providers adhere to a specific interface, meaning you can swap out one provider for another without having to change your code.
10
+
11
+ ## Usage
12
+
13
+ Before you get started configuring Kuby, you'll need to create a cluster and service account for accessing said cluster. The service account should have owner-level permissions to be able to create cluster-level resources. The JSON credentials file mentioned below can be obtained by creating a key for the service account.
14
+
15
+ Enable the GKE provider like so:
16
+
17
+ ```ruby
18
+ require 'kuby/gke'
19
+
20
+ Kuby.define('MyApp') do
21
+ environment(:production) do
22
+ kubernetes do
23
+
24
+ provider :gke do
25
+ # The ID of the GCP project that houses your cluster.
26
+ project_id 'my-project-id'
27
+
28
+ # The name of your cluster.
29
+ cluster_name 'my-cluster-name'
30
+
31
+ # The availability zone your cluster is in, eg. us-central1-a
32
+ zone 'my-zone'
33
+
34
+ # The path to a JSON file containing credentials for an actor
35
+ # that has access to the cluster, most likely a service account.
36
+ keyfile '/path/to/keyfile.json'
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ ```
43
+
44
+ Once configured, you should be able to run all the Kuby rake tasks as you would with any provider.
45
+
46
+ ## License
47
+
48
+ Licensed under the MIT license. See LICENSE for details.
49
+
50
+ ## Authors
51
+
52
+ * Cameron C. Dutro: http://github.com/camertron
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+ require 'rubygems/package_task'
4
+
5
+ require 'kuby/gke'
6
+
7
+ Bundler::GemHelper.install_tasks
8
+
9
+ task default: :spec
10
+
11
+ desc 'Run specs'
12
+ RSpec::Core::RakeTask.new do |t|
13
+ t.pattern = './spec/**/*_spec.rb'
14
+ end
data/kuby-gke.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), 'lib')
2
+ require 'kuby/gke/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'kuby-gke'
6
+ s.version = ::Kuby::GKE::VERSION
7
+ s.authors = ['Cameron Dutro']
8
+ s.email = ['camertron@gmail.com']
9
+ s.homepage = 'http://github.com/getkuby/kuby-gke'
10
+
11
+ s.description = s.summary = 'Google Kubernetes Engine (GKE) provider for Kuby.'
12
+
13
+ s.platform = Gem::Platform::RUBY
14
+
15
+ s.add_dependency 'kube-dsl', '~> 0.7'
16
+ s.add_dependency 'google-cloud-container', '~> 1.3'
17
+ s.add_dependency 'gke-auth-plugin-rb', '~> 0.1'
18
+ s.add_dependency 'kubernetes-cli', '~> 0.5'
19
+
20
+ s.require_path = 'lib'
21
+ s.files = Dir['{lib,spec}/**/*', 'Gemfile', 'LICENSE', 'CHANGELOG.md', 'README.md', 'Rakefile', 'kuby-gke.gemspec']
22
+ end
@@ -0,0 +1,21 @@
1
+ require 'kube-dsl'
2
+ require 'digest'
3
+
4
+ module Kuby
5
+ module GKE
6
+ class Config
7
+ extend ::KubeDSL::ValueFields
8
+
9
+ value_fields :project_id, :cluster_name, :zone, :keyfile
10
+
11
+ def hash_value
12
+ keyfile_hash = Digest::SHA256.hexdigest(
13
+ File.exist?(keyfile) ? File.read(keyfile) : ""
14
+ )
15
+
16
+ parts = [project_id, cluster_name, zone, keyfile, keyfile_hash]
17
+ Digest::SHA256.hexdigest(parts.join(':'))
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,79 @@
1
+ require 'gke-auth-plugin-rb'
2
+
3
+ module Kuby
4
+ module GKE
5
+ class Kubeconfig
6
+ attr_reader :project_id, :cluster_name, :zone, :endpoint, :ca_cert
7
+
8
+ def self.from(config:, cluster_def:)
9
+ new(
10
+ project_id: config.project_id,
11
+ cluster_name: config.cluster_name,
12
+ zone: config.zone,
13
+ endpoint: cluster_def['endpoint'],
14
+ ca_cert: cluster_def['master_auth']['cluster_ca_certificate']
15
+ )
16
+ end
17
+
18
+ def initialize(project_id:, cluster_name:, zone:, endpoint:, ca_cert:)
19
+ @project_id = project_id
20
+ @cluster_name = cluster_name
21
+ @zone = zone
22
+ @endpoint = endpoint
23
+ @ca_cert = ca_cert
24
+ end
25
+
26
+ def generate
27
+ {
28
+ "apiVersion" => "v1",
29
+ "kind" => "Config",
30
+ "clusters" => clusters,
31
+ "contexts" => contexts,
32
+ "current-context" => id_triple,
33
+ "preferences" => {},
34
+ "users" => users
35
+ }
36
+ end
37
+
38
+ private
39
+
40
+ def clusters
41
+ [{
42
+ "cluster" => {
43
+ "certificate-authority-data" => ca_cert,
44
+ "server" => "https://#{endpoint}"
45
+ },
46
+ "name" => id_triple
47
+ }]
48
+ end
49
+
50
+ def contexts
51
+ [{
52
+ "context" => {
53
+ "cluster" => id_triple,
54
+ "user" => id_triple
55
+ },
56
+ "name" => id_triple
57
+ }]
58
+ end
59
+
60
+ def users
61
+ [{
62
+ "name" => id_triple,
63
+ "user" => {
64
+ "exec" => {
65
+ "apiVersion" => "client.authentication.k8s.io/v1beta1",
66
+ "command" => GKEAuthPluginRb.executable,
67
+ "provideClusterInfo" => true,
68
+ "interactiveMode" => "Never"
69
+ }
70
+ }
71
+ }]
72
+ end
73
+
74
+ def id_triple
75
+ @id_triple ||= "#{project_id}_#{zone}_#{cluster_name}"
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,106 @@
1
+ require 'fileutils'
2
+ require 'google/cloud/container'
3
+ require 'google/cloud/container/v1'
4
+ require 'tmpdir'
5
+ require 'yaml'
6
+
7
+ module Kuby
8
+ module GKE
9
+ class Provider < Kuby::Kubernetes::Provider
10
+ STORAGE_CLASS_NAME = 'default'.freeze
11
+
12
+ attr_reader :config
13
+
14
+ def configure(&block)
15
+ config.instance_eval(&block)
16
+ end
17
+
18
+ def kubeconfig_path
19
+ File.join(
20
+ kubeconfig_dir,
21
+ "#{environment.app_name.downcase}-#{config.hash_value}-kubeconfig.yaml"
22
+ )
23
+ end
24
+
25
+ def storage_class_name
26
+ STORAGE_CLASS_NAME
27
+ end
28
+
29
+ def kubernetes_cli
30
+ @kubernetes_cli ||= begin
31
+ refresh_kubeconfig
32
+
33
+ super.tap do |cli|
34
+ cli.env['GOOGLE_APPLICATION_CREDENTIALS'] = config.keyfile
35
+ end
36
+ end
37
+ end
38
+
39
+ def deploy
40
+ with_env({ 'GOOGLE_APPLICATION_CREDENTIALS' => config.keyfile }) do
41
+ super
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def after_initialize
48
+ @config = Config.new
49
+ @kubeconfig_refreshed = false
50
+ end
51
+
52
+ def client
53
+ @client ||= Google::Cloud::Container.cluster_manager do |client_config|
54
+ client_config.credentials = config.keyfile
55
+ end
56
+ end
57
+
58
+ def refresh_kubeconfig
59
+ return unless should_refresh_kubeconfig?
60
+
61
+ FileUtils.mkdir_p(kubeconfig_dir)
62
+
63
+ Kuby.logger.info('Refreshing kubeconfig...')
64
+
65
+ request = Google::Cloud::Container::V1::GetClusterRequest.new(
66
+ name: "projects/#{config.project_id}/locations/#{config.zone}/clusters/#{config.cluster_name}"
67
+ )
68
+
69
+ cluster = client.get_cluster(request)
70
+ kubeconfig = Kubeconfig.from(config: config, cluster_def: cluster)
71
+
72
+ File.write(kubeconfig_path, YAML.dump(kubeconfig.generate))
73
+
74
+ @kubeconfig_refreshed = true
75
+
76
+ Kuby.logger.info('Successfully refreshed kubeconfig!')
77
+ end
78
+
79
+ def should_refresh_kubeconfig?
80
+ return false if @kubeconfig_refreshed
81
+ !File.exist?(kubeconfig_path) || !can_communicate_with_cluster?
82
+ end
83
+
84
+ def can_communicate_with_cluster?
85
+ cli = ::KubernetesCLI.new(kubeconfig_path)
86
+ cli.env['GOOGLE_APPLICATION_CREDENTIALS'] = config.keyfile
87
+ cli.send(:backticks, [cli.executable, '--kubeconfig', kubeconfig_path, 'get', 'ns'])
88
+ cli.last_status.success?
89
+ end
90
+
91
+ def kubeconfig_dir
92
+ @kubeconfig_dir ||= File.join(
93
+ Dir.tmpdir, 'kuby-gke'
94
+ )
95
+ end
96
+
97
+ def with_env(new_env)
98
+ old_env = ENV.to_h
99
+ ENV.replace(old_env.merge(new_env))
100
+ yield
101
+ ensure
102
+ ENV.replace(old_env)
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,5 @@
1
+ module Kuby
2
+ module GKE
3
+ VERSION = '0.1.0'.freeze
4
+ end
5
+ end
data/lib/kuby/gke.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'kuby'
2
+ require 'kuby/gke/provider'
3
+
4
+ module Kuby
5
+ module GKE
6
+ autoload :Config, 'kuby/gke/config'
7
+ autoload :Kubeconfig, 'kuby/gke/kubeconfig'
8
+ end
9
+ end
10
+
11
+ Kuby.register_provider(:gke, Kuby::GKE::Provider)
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kuby-gke
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Cameron Dutro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-06-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: kube-dsl
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: google-cloud-container
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: gke-auth-plugin-rb
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: kubernetes-cli
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.5'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.5'
69
+ description: Google Kubernetes Engine (GKE) provider for Kuby.
70
+ email:
71
+ - camertron@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - CHANGELOG.md
77
+ - Gemfile
78
+ - LICENSE
79
+ - README.md
80
+ - Rakefile
81
+ - kuby-gke.gemspec
82
+ - lib/kuby/gke.rb
83
+ - lib/kuby/gke/config.rb
84
+ - lib/kuby/gke/kubeconfig.rb
85
+ - lib/kuby/gke/provider.rb
86
+ - lib/kuby/gke/version.rb
87
+ homepage: http://github.com/getkuby/kuby-gke
88
+ licenses: []
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubygems_version: 3.4.5
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: Google Kubernetes Engine (GKE) provider for Kuby.
109
+ test_files: []