ADD test devops

This commit is contained in:
Jasder 2020-07-10 14:08:16 +08:00
parent dbafd32d0b
commit e74bba9092
16 changed files with 317 additions and 6 deletions

View File

@ -0,0 +1,64 @@
class DevOps::CloudAccountsController < ApplicationController
before_action :require_login
before_action :find_project
def create
ActiveRecord::Base.transaction do
DevOps::CreateCloudAccountForm.new(devops_params).validate!
logger.info "######### devops_params: #{devops_params}"
logger.info "######### ......: #{(IPAddr.new devops_params[:ip_num]).to_i}"
logger.info "######### ......: #{DevopsCloudAccount.encrypted_secret(devops_params[:secret])}"
# 1. 保存华为云服务器帐号
logger.info "######### ......ff: #{devops_params.merge(ip_num: IPAddr.new(devops_params[:ip_num]).to_i, secret: DevopsCloudAccount.encrypted_secret(devops_params[:secret]))}"
create_params = devops_params.merge(ip_num: IPAddr.new(devops_params[:ip_num]).to_i, secret: DevopsCloudAccount.encrypted_secret(devops_params[:secret]))
logger.info "######### create_params: #{create_params}"
cloud_account = DevopsCloudAccount.new(create_params)
cloud_account.user = current_user
cloud_account.save
# 2. 生成oauth2应用程序的client_id和client_secrete
gitea_oauth = Gitea::Oauth2::CreateService.call(current_user.gitea_token, {name: "pipeline", redirect_uris: [cloud_account.drone_url]})
logger.info "######### gitea_oauth: #{gitea_oauth}"
oauth = Oauth.new(client_id: gitea_oauth['client_id'],
client_secret: gitea_oauth['client_secret'],
redirect_uri: gitea_oauth['redirect_uris'],
gitea_oauth_id: gitea_oauth['id'],
user_id: current_user.id,
project_id: devops_params[:project_id])
oauth.save
rpc_secret = SecureRandom.hex 16
logger.info "######### rpc_secret: #{rpc_secret}"
# 3. 创建drone server
drone_server_cmd = DevOps::Drone::Server.new(oauth.client_id, oauth.client_secret, cloud_account.drone_host, rpc_secret).generate_cmd
logger.info "######### drone_server_cmd: #{drone_server_cmd}"
# 4. 创建drone client
drone_client_cmd = DevOps::Drone::Client.new(oauth.client_id, cloud_account.drone_ip, rpc_secret).generate_cmd
logger.info "######### drone_client_cmd: #{drone_client_cmd}"
# 5. 登录远程服务器启动drone服务
result = DevOps::Drone::Start.new(cloud_account.account, cloud_account.visible_secret, cloud_account.drone_ip, drone_server_cmd, drone_client_cmd).run
logger.info "######### result: #{result}"
redirect_url = "#{Gitea.gitea_config[:domain]}/login/oauth/authorize?client_id=#{oauth.client_id}&redirect_uri=#{cloud_account.drone_url}/login&response_type=code"
if result
render_ok(redirect_url: redirect_url)
else
render_error('激活失败')
end
end
rescue Exception => ex
render_error(ex.message)
end
private
def devops_params
params.permit(:account, :secret, :ip_num, :project_id)
end
def find_project
@project = Project.find_by_id params[:project_id]
render_not_found("未找到project_id为:#{params[:project_id]}相关的项目") if @project.blank?
end
end

View File

@ -0,0 +1,9 @@
class DevOps::CreateCloudAccountForm
include ActiveModel::Model
attr_accessor :project_id, :ip_num, :account, :secret
validates :project_id, :account, :secret, presence: true
validates :ip_num, presence: true, format: { with: CustomRegexp::IP, multiline: true, message: 'IP 地址格式不对' }
end

View File

@ -0,0 +1,2 @@
module DevOpsHelper
end

View File

@ -5,4 +5,5 @@ module CustomRegexp
NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/
PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{8,16}\z/
URL = /\Ahttps?:\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]\z/
end
IP = /^((\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/
end

View File

@ -0,0 +1,23 @@
class DevOps::Drone::Ci
attr_reader :host, :username, :password
# host: drone server's ip
# username: drone server's account
# password: drone server's password
# eq:
# DevOps::Drone::Ci.new(@cloud_account.drone_ip, @cloud_account.account, @cloud_account.visible_secret).get_token
def initialize(host, username, password)
@host = host
@username = username
@password = password
end
def get_token
`sshpass -p #{password} ssh -o "StrictHostKeyChecking no" #{username}@#{host} "#{cmd}"`
end
private
def cmd
"cd ..; cd var/lib/drone/; sqlite3 database.sqlite; .dump; select user_hash from users where user_login=#{username} "
end
end

View File

@ -0,0 +1,39 @@
class DevOps::Drone::Client
attr_reader :client_id, :drone_ip, :rpc_secret
# client_id: user's client_id from oauth
# drone_ip: 云服务器IP地址, eq: 173.65.32.21
# eq:
# DevOps::Drone::Client.new(current_user.oauth.client_id, 'drone_ip').generate_cmd
def initialize(client_id, drone_ip, rpc_secret)
@client_id = client_id
@drone_ip = drone_ip
@rpc_secret = rpc_secret
end
def run
`docker run -d \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DRONE_RPC_SERVER=drone-server-#{client_id}:9000 \
-e DRONE_RPC_SECRET=#{rpc_secret} \
-e DRONE_RUNNER_NAME=#{drone_ip} \
--restart always \
--name drone-agent--#{client_id} \
--net="bridge" \
drone/drone-runner-docker:1
`
end
def generate_cmd
"docker run -d \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DRONE_RPC_SERVER=drone-server-#{client_id}:9000 \
-e DRONE_RPC_SECRET=#{rpc_secret} \
-e DRONE_RUNNER_NAME=#{drone_ip} \
--restart always \
--name drone-agent--#{client_id} \
--net='bridge' \
drone/drone-runner-docker:1
"
end
end

View File

@ -0,0 +1,59 @@
class DevOps::Drone::Server
attr_reader :client_id, :client_secret, :drone_host, :rpc_secret
# client_id: user's client_id from oauth
# client_secret: user's client_id from oauth
# drone_host: 云服务器地址eq: 173.53.21.31:80
# eg:
# DevOps::Drone::Server.new(current_user.oauth.client_id, current_user.oauth.client_secret, 'drone_host').generate_cmd
def initialize(client_id, client_secret, drone_host, rpc_secret)
@client_id = client_id
@drone_host = drone_host
@rpc_secret = rpc_secret
@client_secret = client_secret
end
def run
`
docker run \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DRONE_GITEA_SERVER=#{gitea_url} \
-e DRONE_GITEA_CLIENT_ID=#{client_id} \
-e DRONE_GITEA_CLIENT_SECRET=#{client_secret} \
-e DRONE_RPC_SECRET=#{rpc_secret} \
-e DRONE_SERVER_HOST=#{drone_host} \
-e DRONE_SERVER_PROTO=http \
-p "80:80" \
-p "9000:9000" \
--restart=always \
--detach=true \
--name=drone-server-#{client_id} \
--net="bridge" \
drone/drone:1
`
end
def generate_cmd
"service docker start; docker run \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DRONE_GITEA_SERVER=#{gitea_url} \
-e DRONE_GITEA_CLIENT_ID=#{client_id} \
-e DRONE_GITEA_CLIENT_SECRET=#{client_secret} \
-e DRONE_RPC_SECRET=#{rpc_secret} \
-e DRONE_SERVER_HOST=#{drone_host} \
-e DRONE_SERVER_PROTO=http \
-p '80:80' \
-p '9000:9000' \
--restart=always \
--detach=true \
--name=drone-server-#{client_id} \
--net='bridge' \
drone/drone:1
"
end
private
def gitea_url
Gitea.gitea_config[:domain]
end
end

View File

@ -0,0 +1,22 @@
class DevOps::Drone::Start
attr_reader :drone_username, :drone_password, :drone_host, :drone_server_cmd, :drone_client_cmd
# drone_username="XXXX" 云服务器登录用户名
# drone_password="XXXXX" 云服务器用户密码
# drone_host="" 云服务器地址
# eq:
# drone_server_cmd = DevOps::Drone::Server.new('client_id', 'client_secret', 'drone_url').generate_cmd
# drone_client_cmd = DevOps::Drone::Client.new('client_id', 'server_url').generate_cmd
# DevOps::Drone::Start.new(drone_username, drone_password, 'drone_host', drone_server_cmd, drone_client_cmd).run
def initialize(drone_username, drone_password, drone_host, drone_server_cmd, drone_client_cmd)
@drone_username = drone_username
@drone_password = drone_password
@drone_host = drone_host
@drone_server_cmd = drone_server_cmd
@drone_client_cmd = drone_client_cmd
end
def run
`sshpass -p #{drone_password} ssh -o "StrictHostKeyChecking no" #{drone_username}@#{drone_host} "#{drone_server_cmd} && #{drone_client_cmd}"`
end
end

View File

@ -0,0 +1,25 @@
class DevopsCloudAccount < ApplicationRecord
belongs_to :project
belongs_to :user
def drone_host
[drone_ip, ":80"].join
end
def drone_ip
IPAddr.new(self.ip_num, Socket::AF_INET).to_s
end
def drone_url
["http://", drone_host].join
end
def visible_secret
Base64.decode64(secret)
end
def self.encrypted_secret(str)
Base64.encode64(str.strip).gsub(/\n/, '')
end
end

5
app/models/oauth.rb Normal file
View File

@ -0,0 +1,5 @@
# for oauth2 application
class Oauth < ApplicationRecord
belongs_to :project
belongs_to :user
end

View File

@ -161,7 +161,7 @@ class Project < ApplicationRecord
member&.roles&.last&.name || permission
end
def fork_project
def fork_project
Project.find_by(id: self.forked_from_project_id)
end

View File

@ -21,9 +21,6 @@ class Gitea::ClientService < ApplicationService
def post(url, params={})
puts "[gitea] request params: #{params}"
request_url = [api_url, url].join('').freeze
Rails.logger.info("######_____api____request_url_______###############{request_url}")
Rails.logger.info("######_____api____request_params_______###############{params}")
auth_token = authen_params(params[:token])
response = conn(auth_token).post do |req|
req.url "#{request_url}"

View File

@ -0,0 +1,41 @@
# creates a new OAuth2 application
class Gitea::Oauth2::CreateService < Gitea::ClientService
attr_reader :token, :params
# params:
# {
# "name": "string",
# "redirect_uris": [
# "string"
# ]
# }
# ep: Gitea::OAuth2::CreateService.call(current_user.gitea_token, {name: 'oauth_name', redirect_uris: ['url']})
# return values example:
# {
# "client_id": "string",
# "client_secret": "string",
# "created": "2020-07-08T03:12:49.960Z",
# "id": 0,
# "name": "string",
# "redirect_uris": [
# "string"
# ]
# }
def initialize(token, params)
@token = token
@params = params
end
def call
post(url, request_params)
end
private
def url
"/user/applications/oauth2".freeze
end
def request_params
params.merge(token: token, data: params).compact
end
end

View File

@ -1,5 +1,5 @@
Rails.application.routes.draw do
require 'sidekiq/web'
require 'admin_constraint'
@ -16,6 +16,10 @@ Rails.application.routes.draw do
resources :edu_settings
scope '/api' do
namespace :dev_ops do
resources :cloud_accounts, only: [:create]
end
resources :composes do
resources :compose_projects, only: [:create, :destroy]
end

View File

@ -0,0 +1,14 @@
class CreateDevopsCloudAccounts < ActiveRecord::Migration[5.2]
def change
create_table :devops_cloud_accounts do |t|
t.integer :project_id, null: false
t.integer :user_id, null: false
t.integer :ip_num, null: false
t.string :account, null: false
t.string :secret, null: false
t.timestamps
end
add_index :devops_cloud_accounts, [:project_id, :user_id, :ip_num]
end
end

View File

@ -0,0 +1,6 @@
class AddGiteaOauthIdAndProjectIdToOauths < ActiveRecord::Migration[5.2]
def change
add_column :oauths, :gitea_oauth_id, :integer
add_column :oauths, :project_id, :integer
end
end