Last active
August 29, 2015 14:11
-
-
Save minimum2scp/97c288ece318e973df4f to your computer and use it in GitHub Desktop.
gcloud+peco。プロジェクト一覧からpecoで選択してgcloud config setしたり、VMインスタンス一覧からpecoで選択してgcloud compute sshしたり。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
peco-gcloud-project-widget (){ | |
peco-gcloud project -z | anyframe-action-put | |
} | |
zle -N peco-gcloud-project-widget | |
bindkey '^gp' peco-gcloud-project-widget | |
peco-gcloud-ssh-widget (){ | |
peco-gcloud ssh -z | anyframe-action-put | |
} | |
zle -N peco-gcloud-ssh-widget | |
bindkey '^gs' peco-gcloud-ssh-widget |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env ruby | |
require 'json' | |
require 'open3' | |
require 'yaml/store' | |
require 'etc' | |
require 'singleton' | |
#require 'pry' | |
require 'thor' | |
require 'text-table' | |
module PecoGcloud | |
class Cli < Thor | |
class_option :zsh_widget, :type => :boolean, :default => false, :aliases => ['-z'] | |
option :project, :type => :string, :aliases => ['-p'] | |
desc "ssh", "select vm instance by peco, and run gcloud compute ssh" | |
long_desc <<-LONG_DESC | |
execute `gcloud compute instances list`, | |
and filter the results by `peco`, | |
and execute or print `gcloud compute ssh` | |
LONG_DESC | |
def ssh | |
project = options[:project] || Config.instance.get('core/project') | |
vm_instance = VmInstanceList.load(project: project).select(multi:false, project:project) | |
if options[:zsh_widget] | |
puts vm_instance.build_ssh_cmd | |
else | |
system vm_instance.build_ssh_cmd | |
end | |
rescue => e | |
if !options[:zsh_widget] | |
raise e | |
end | |
end | |
desc "project", "select project and run gcloud config set project" | |
long_desc <<-LONG_DESC | |
execute `gcloud preview projects list`, | |
and filter the results by `peco`, | |
and execute or print `gcloud config set project` | |
see https://cloud.google.com/sdk/gcloud/reference/preview/ | |
about `gcloud preview ...` | |
LONG_DESC | |
def project | |
project = ProjectList.load.select(multi:false) | |
if options[:zsh_widget] | |
puts project.build_config_set_project_cmd | |
else | |
system project.build_config_set_project_cmd | |
end | |
rescue => e | |
if !options[:zsh_widget] | |
raise e | |
end | |
end | |
desc "gencache", "cache all projects and instances" | |
def gencache | |
puts "loading projects..." | |
project_list = ProjectList.load(force: true) | |
puts "found #{project_list.projects.size} projects." | |
project_list.projects.each do |project| | |
puts "loading VM instances on project: #{project.title} (#{project.id})" | |
vm_instance_list = VmInstanceList.load(force:true, project: project.id) | |
puts "found #{vm_instance_list.instances.size} vm instances" | |
end | |
end | |
end | |
## gcloud preview projects list | |
class Project < Struct.new(:id, :title, :number) | |
def build_config_set_project_cmd | |
%Q[gcloud config set project "#{id}"] | |
end | |
end | |
## gcloud preview projects list | |
class ProjectList < Struct.new(:projects) | |
def select(multi:false) | |
selected = Open3.popen3('peco'){|stdin, stdout, stderr, wait_thr| | |
stdin.puts to_table | |
stdin.close_write | |
stdout.readlines | |
}.map{ |line| | |
projects.find{|prj| prj.id == line.chomp.gsub(/(^\|\s*|\s*\|$)/, '').split(/\s*\|\s*/).first } | |
}.reject{|prj| | |
prj.id == "id" || prj.id =~ /^[+-]+$/ | |
} | |
if multi | |
selected | |
elsif selected.size == 1 | |
selected.first | |
else | |
raise "please select 1 Project" | |
end | |
end | |
def to_table | |
fields = Project.new.members | |
table = Text::Table.new( :head => fields, :rows => projects.map{|i| fields.map{|f| i.send(f)}} ) | |
end | |
class << self | |
def preview_installed? | |
! (%x[gcloud components list 2>/dev/null | grep -F '| preview '] =~ /Not Installed/) | |
end | |
def load(force:false) | |
if !preview_installed? | |
abort 'gcloud component "preview" is not installed, please install by `gcloud components update preview`' | |
end | |
if force || ! defined?(@@projects) | |
@@projects = Cache.instance.get_or_set('projects', expire:24*60*60) do | |
JSON.parse(%x[gcloud preview projects list --format json]).map{|prj| | |
Project.new(prj['projectId'], prj['title'], prj['projectNumber']) | |
} | |
end | |
end | |
ProjectList.new(@@projects) | |
end | |
end | |
end | |
## gcloud compute ssh ... | |
## gcloud compute instances ... | |
class VmInstance < Struct.new(:project, :name, :zone, :machine_type, :internal_ip, :external_ip, :status) | |
def build_ssh_cmd | |
%Q[gcloud compute --project="#{project}" ssh --zone="#{zone}" "#{name}"] | |
end | |
end | |
## gcloud compute instances ... | |
class VmInstanceList < Struct.new(:instances) | |
def select(multi:false, project:false) | |
selected_instances = Open3.popen3('peco'){|stdin, stdout, stderr, wait_thr| | |
stdin.puts to_table(with_project: !!project) | |
stdin.close_write | |
stdout.readlines | |
}.map{ |line| | |
selected = line.chomp.gsub(/(^\|\s*|\s*\|$)/, '').split(/\s*\|\s*/) | |
instances.find{|i| project ? (i.name == selected[0]) : (i.name == selected[1] && i.project == selected[0]) } | |
}.reject{|i| | |
i.name == "name" || i.name =~ /^[+-]+$/ | |
} | |
if multi | |
selected_instances | |
elsif selected_instances.size == 1 | |
selected_instances.first | |
else | |
raise "please select 1 VM instance" | |
end | |
end | |
def to_table(with_project:false) | |
fields = VmInstance.new.members | |
fields.reject!{|i| i == :project} if with_project | |
table = Text::Table.new( :head => fields, :rows => instances.map{|i| fields.map{|f| i.send(f)}} ) | |
end | |
class << self | |
def load(force:false, project:nil) | |
if project | |
if force || ! defined?(@@instances) | |
@@instances = Cache.instance.get_or_set("instances/#{project}") do | |
JSON.parse(%x[gcloud compute instances list --project=#{project} --sort-by name --format json]).map{|i| | |
VmInstance.new(project, i['name'], i['zone'], i['machineType'], i['networkInterfaces'].first['networkIP'], i['networkInterfaces'].first['accessConfigs'].first['natIP'], i['status']) | |
} | |
end | |
end | |
VmInstanceList.new(@@instances) | |
else | |
project_list = ProjectList.load | |
## TODO: refactoring | |
@@instances = project_list.projects.map{|project| | |
Cache.instance.get_or_set("instances/#{project.id}") do | |
pid = fork { | |
VmInstanceList.load(force:force, project:project.id) | |
} | |
Process.waitpid pid | |
Cache.instance.get("instances/#{project.id}") | |
end | |
}.flatten | |
VmInstanceList.new(@@instances) | |
end | |
end | |
end | |
end | |
## gcloud config ... | |
class Config | |
include Singleton | |
def initialize | |
@config = JSON.parse %x[gcloud config list --all --format json] | |
end | |
def get(arg) | |
sec, key = arg.split('/', 2) | |
@config[sec][key] | |
end | |
end | |
class Cache | |
include Singleton | |
def initialize(path:"/tmp/gcloud-cache.#{Etc.getlogin}.yaml") | |
@db = YAML::Store.new(path) | |
end | |
def get_or_set(key, expire:24*60*60, &block) | |
get(key) || set(key, block.call, expire:expire) | |
end | |
def set(key, value, expire:24*60*60) | |
@db.transaction{ @db[key] = {value: value, expire: Time.now+expire}; value } | |
end | |
def get(key) | |
@db.transaction{ @db[key] && @db[key][:expire] > Time.now ? @db[key][:value] : nil } | |
end | |
end | |
end | |
PecoGcloud::Cli.start |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment