diff --git a/Gemfile.lock b/Gemfile.lock
index 3e8640c..6d65f36 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -5,7 +5,7 @@ PATH
bcrypt (~> 3.1.5)
bundler (~> 1.5)
coffee-rails (~> 4.0.0)
- fluentd (= 0.10.46)
+ fluentd (~> 0.10.48)
font-awesome-rails
haml-rails (~> 0.5.3)
httpclient
@@ -81,14 +81,14 @@ GEM
factory_girl_rails (4.4.1)
factory_girl (~> 4.4.0)
railties (>= 3.0.0)
- fluentd (0.10.46)
+ fluentd (0.10.48)
cool.io (>= 1.1.1, < 2.0.0, != 1.2.0)
http_parser.rb (>= 0.5.1, < 0.7.0)
json (>= 1.4.3)
msgpack (>= 0.4.4, < 0.6.0, != 0.5.3, != 0.5.2, != 0.5.1, != 0.5.0)
sigdump (~> 0.2.2)
yajl-ruby (~> 1.0)
- font-awesome-rails (4.0.3.1)
+ font-awesome-rails (4.1.0.0)
railties (>= 3.2, < 5.0)
haml (4.0.5)
tilt
diff --git a/app/controllers/fluentd/agents_controller.rb b/app/controllers/fluentd/agents_controller.rb
new file mode 100644
index 0000000..7eb5967
--- /dev/null
+++ b/app/controllers/fluentd/agents_controller.rb
@@ -0,0 +1,37 @@
+class Fluentd::AgentsController < ApplicationController
+ before_action :find_fluentd
+
+ def show
+ end
+
+ def start
+ unless @fluentd.agent.start
+ flash[:error] = t("error.fluentd_start_failed")
+ end
+ redirect_to fluentd_agent_path(@fluentd), status: 303 # 303 is change HTTP Verb GET
+ end
+
+ def stop
+ unless @fluentd.agent.stop
+ flash[:error] = t("error.fluentd_stop_failed")
+ end
+ redirect_to fluentd_agent_path(@fluentd), status: 303 # 303 is change HTTP Verb GET
+ end
+
+ def restart
+ unless @fluentd.agent.restart
+ flash[:error] = t("error.fluentd_restart_failed")
+ end
+ redirect_to fluentd_agent_path(@fluentd), status: 303 # 303 is change HTTP Verb GET
+ end
+
+ def log
+ render text: @fluentd.agent.log, content_type: "text/plain"
+ end
+
+ private
+
+ def find_fluentd
+ @fluentd = Fluentd.find(params[:fluentd_id])
+ end
+end
diff --git a/app/controllers/fluentd/daemons_controller.rb b/app/controllers/fluentd/daemons_controller.rb
deleted file mode 100644
index 1103cf5..0000000
--- a/app/controllers/fluentd/daemons_controller.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-class Fluentd::DaemonsController < ApplicationController
- before_action :login_required
- before_action :fluentd
-
- def show
- end
-
- def start
- fluentd.start
- render :show
- end
-
- def stop
- fluentd.stop
- render :show
- end
-
- def reload
- fluentd.reload
- render :show
- end
-
- def log
- render text: fluentd.log, content_type: "text/plain"
- end
-
- private
-
- def fluentd
- @fluentd ||= Fluentd.new(Rails.root + "tmp" + "fluentd") # TODO
- end
-end
diff --git a/app/controllers/fluentd_controller.rb b/app/controllers/fluentd_controller.rb
index 6531678..dbad858 100644
--- a/app/controllers/fluentd_controller.rb
+++ b/app/controllers/fluentd_controller.rb
@@ -1,7 +1,47 @@
class FluentdController < ApplicationController
- before_action :login_required
+ before_action :find_fluentd, only: [:edit, :update, :destroy]
def index
- @daemons = [Fluentd.new(Rails.root + "tmp" + "fluentd")] # TODO
+ @fluentds = Fluentd.all
+ end
+
+ def new
+ @fluentd = Fluentd.new(Fluentd::Agent::Fluentd::DEFAULT_OPTIONS) # TODO: not fluentd type
+ end
+
+ def create
+ @fluentd = Fluentd.new(fluentd_params)
+ unless @fluentd.save
+ return render :new
+ end
+ redirect_to fluentd_index_path
+ end
+
+ def edit
+ end
+
+ def update
+ # TODO: should restart if changed file path? or just do "dirty" flagged?
+ @fluentd.update_attributes(fluentd_params)
+ unless @fluentd.save
+ return render :edit
+ end
+ redirect_to fluentd_index_path
+ end
+
+ def destroy
+ @fluentd.agent.stop if @fluentd.agent.running?
+ @fluentd.destroy
+ redirect_to fluentd_index_path
+ end
+
+ private
+
+ def find_fluentd
+ @fluentd = Fluentd.find(params[:id])
+ end
+
+ def fluentd_params
+ params.require(:fluentd).permit(:log_file, :pid_file, :config_file, :variant)
end
end
diff --git a/app/helpers/fluentd/daemons_helper.rb b/app/helpers/fluentd/daemons_helper.rb
deleted file mode 100644
index 977e042..0000000
--- a/app/helpers/fluentd/daemons_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module Fluentd::DaemonsHelper
-end
diff --git a/app/models/fluentd.rb b/app/models/fluentd.rb
index 91e8e01..3b113d2 100644
--- a/app/models/fluentd.rb
+++ b/app/models/fluentd.rb
@@ -1,109 +1,64 @@
-Bundler.require(:default, :development)
+class Fluentd < ActiveRecord::Base
+ before_validation :expand_paths
+ validates :variant, inclusion: { in: proc { Fluentd.variants } }
+ validates :log_file, presence: true
+ validates :pid_file, presence: true
+ validates :config_file, presence: true
+ validate :validate_permissions
-require 'fluent/log'
-require 'fluent/env'
-require 'fluent/version'
-require 'fluent/supervisor'
-
-class Fluentd
- attr_reader :root_dir
-
- def initialize(root_dir)
- @root_dir = root_dir
- FileUtils.mkdir_p @root_dir
+ def self.variants
+ %w(fluentd) # TODO:
end
- def pid_file
- File.join(root_dir, "fluentd.pid")
+ def fluentd?
+ variant == "fluentd"
end
- def pid
- return unless File.exists?(pid_file)
- File.read(pid_file)
+ def td_agent?
+ variant == "td-agent"
end
- def log_file
- File.join(root_dir, "fluentd.log")
- end
-
- def config_file
- file = File.join(root_dir, "fluentd.conf")
- unless File.exists?(file)
- File.open(file, "w") {|f| f.write "\ntype forward\n" } # TODO
- end
- file
- end
-
- def plugin_dir
- dir = File.join(root_dir, "fluentd", "plugins")
- unless Dir.exist?(dir)
- FileUtils.mkdir_p(dir)
- end
- dir
- end
-
- def options
- # TODO: https://github.com/fluent/fluentd/pull/315
- {
- :config_path => Fluent::DEFAULT_CONFIG_PATH,
- :plugin_dirs => [Fluent::DEFAULT_PLUGIN_DIR],
- :log_level => Fluent::Log::LEVEL_INFO,
- :log_path => nil,
- :daemonize => false,
- :libs => [],
- :setup_path => nil,
- :chuser => nil,
- :chgroup => nil,
- :suppress_interval => 0,
- :suppress_repeated_stacktrace => false,
- :use_v1_config => false,
- }.merge({
- :use_v1_config => true,
- :plugin_dirs => [plugin_dir],
- :config_path => config_file,
- :daemonize => pid_file,
- :log_path => log_file,
- :log_level => Fluent::Log::LEVEL_INFO,
+ def agent
+ klass = variant.underscore.camelize
+ @agent ||= Agent.const_get(klass).new({
+ :pid_file => pid_file,
+ :log_file => log_file,
+ :config_file => config_file,
})
end
- def running?
- pid && system("/bin/kill -0 #{pid}", :out => File::NULL, :err => File::NULL)
+ def expand_paths
+ %w(pid_file log_file config_file).each do |column|
+ path = send(column)
+ next if path.blank?
+ self.send("#{column}=", File.expand_path(path))
+ end
end
- def start
- return if running?
- spawn("bundle exec fluentd #{options_to_argv(options)}") # TODO
+ def validate_permissions
+ %w(pid_file log_file config_file).each do |column|
+ check_permission(column)
+ end
end
- def stop
- return unless running?
- system("/bin/kill -TERM #{pid}")
- File.unlink(pid_file)
- end
+ def check_permission(column)
+ path = send(column)
+ return if path.blank? # if empty, presence: true will catch it
+ if File.exist?(path)
+ if File.directory?(path)
+ errors.add(column, :is_a_directory)
+ end
- def reload
- return unless running?
- system("/bin/kill -HUP #{pid}")
- end
-
- def log
- File.read log_file # TODO: large log file
- end
-
- def config
- ::Fluentd::Configuration.new(config_file)
- end
-
- private
-
- def options_to_argv(options)
- argv = ""
- argv << " --use-v1-config" if options[:use_v1_config]
- argv << " -c #{options[:config_path]}" if options[:config_path].present?
- argv << " -p #{options[:plugin_dir].first}" if options[:plugin_dir].present?
- argv << " -d #{options[:daemonize]}" if options[:daemonize].present?
- argv << " -o #{options[:log_path]}" if options[:log_path].present?
- argv
+ unless File.writable?(path)
+ errors.add(column, :lack_write_permission)
+ end
+ unless File.readable?(path)
+ errors.add(column, :lack_read_permission)
+ end
+ else
+ unless File.writable?(File.dirname(path))
+ errors.add(column, :lack_write_permission)
+ end
+ end
end
end
diff --git a/app/models/fluentd/agent.rb b/app/models/fluentd/agent.rb
new file mode 100644
index 0000000..e4ac19d
--- /dev/null
+++ b/app/models/fluentd/agent.rb
@@ -0,0 +1,28 @@
+require 'fluent/log'
+require 'fluent/env'
+require 'fluent/version'
+require 'fluent/supervisor'
+require "fluentd/agent/common"
+require "fluentd/agent/fluentd"
+require "fluentd/agent/td_agent"
+require "fluentd/agent/remote"
+
+class Fluentd
+ class Agent
+ # pidfile
+ # td-agent: /var/run/td-agent/td-agent.pid
+ # - https://github.com/treasure-data/td-agent/blob/master/td-agent.logrotate#L10
+ # - https://github.com/treasure-data/td-agent/blob/master/debian/td-agent.init#L25
+ # fluentd: nothing (or --daemon PIDFILE)
+ #
+ # logfile
+ # td-agent: /var/log/td-agent/td-agent.log
+ # - https://github.com/treasure-data/td-agent/blob/master/debian/td-agent.init#L28
+ # fluentd: stdout (or --log LOGFILE)
+ #
+ # config file
+ # td-agent: /etc/td-agent/td-agent.conf
+ # - https://github.com/treasure-data/td-agent/blob/master/debian/td-agent.postinst#L69
+ # fluentd: /etc/fluent/fluent.conf (by fluentd -s)
+ end
+end
diff --git a/app/models/fluentd/agent/common.rb b/app/models/fluentd/agent/common.rb
new file mode 100644
index 0000000..59796ba
--- /dev/null
+++ b/app/models/fluentd/agent/common.rb
@@ -0,0 +1,62 @@
+# pidfile
+# td-agent: /var/run/td-agent/td-agent.pid
+# - https://github.com/treasure-data/td-agent/blob/master/td-agent.logrotate#L10
+# - https://github.com/treasure-data/td-agent/blob/master/debian/td-agent.init#L25
+# fluentd: nothing (or --daemon PIDFILE)
+#
+# logfile
+# td-agent: /var/log/td-agent/td-agent.log
+# - https://github.com/treasure-data/td-agent/blob/master/debian/td-agent.init#L28
+# fluentd: stdout (or --log LOGFILE)
+#
+# config file
+# td-agent: /etc/td-agent/td-agent.conf
+# - https://github.com/treasure-data/td-agent/blob/master/debian/td-agent.postinst#L69
+# fluentd: /etc/fluent/fluent.conf (by fluentd -s)
+
+class Fluentd
+ class Agent
+ module Common
+ attr_reader :extra_options
+
+ def initialize(options = {})
+ @extra_options = options
+ end
+
+ def pid
+ return unless File.exists?(pid_file)
+ File.read(pid_file).to_i rescue nil
+ end
+
+ def wait_process_starting_seconds
+ 10.seconds # wait time for fluentd pidfile created
+ end
+
+ def running?
+ pid && Process.kill(0, pid)
+ end
+
+ def log
+ File.read(log_file) # TODO: large log file
+ end
+
+ def pid_file
+ extra_options[:pid_file] || self.class.default_options[:pid_file]
+ end
+
+ def log_file
+ extra_options[:log_file] || self.class.default_options[:log_file]
+ end
+
+ def config_file
+ extra_options[:config_file] || self.class.default_options[:config_file]
+ end
+
+ %w(start stop restart).each do |method|
+ define_method(method) do
+ raise NotImplementedError
+ end
+ end
+ end
+ end
+end
diff --git a/app/models/fluentd/agent/fluentd.rb b/app/models/fluentd/agent/fluentd.rb
new file mode 100644
index 0000000..8308f11
--- /dev/null
+++ b/app/models/fluentd/agent/fluentd.rb
@@ -0,0 +1,77 @@
+class Fluentd
+ class Agent
+ class Fluentd
+ include Common
+
+ def self.default_options
+ {
+ :pid_file => "/var/run/fluent.pid",
+ :log_file => "/var/log/fluent.log",
+ :config_file => "/etc/fluent/fluent.conf",
+ }
+ end
+
+ def options_to_argv
+ argv = ""
+ argv << " --use-v1-config"
+ argv << " -c #{config_file}"
+ argv << " -d #{pid_file}"
+ argv << " -o #{log_file}"
+ argv
+ end
+
+ def start
+ return true if running?
+ if validate_fluentd_options
+ actual_start
+ end
+ end
+
+ def stop
+ return true unless running?
+ actual_stop
+ end
+
+ def restart
+ return false unless running?
+ actual_restart
+ end
+
+ private
+
+ def validate_fluentd_options
+ system("bundle exec fluentd --dry-run #{options_to_argv}")
+ end
+
+ def actual_start
+ spawn("bundle exec fluentd #{options_to_argv}")
+ wait_starting
+ end
+
+ def actual_stop
+ if Process.kill(:TERM, pid)
+ File.unlink(pid_file)
+ true
+ end
+ end
+
+ def actual_restart
+ Process.kill(:HUP, pid)
+ end
+
+ def wait_starting
+ begin
+ timeout(wait_process_starting_seconds) do
+ loop do
+ break if pid && Process.kill(0, pid)
+ sleep 0.01
+ end
+ end
+ true
+ rescue TimeoutError
+ false
+ end
+ end
+ end
+ end
+end
diff --git a/app/models/fluentd/agent/remote.rb b/app/models/fluentd/agent/remote.rb
new file mode 100644
index 0000000..e5de011
--- /dev/null
+++ b/app/models/fluentd/agent/remote.rb
@@ -0,0 +1,7 @@
+class Fluentd
+ class Agent
+ class Remote # TODO
+ include Common
+ end
+ end
+end
diff --git a/app/models/fluentd/agent/td_agent.rb b/app/models/fluentd/agent/td_agent.rb
new file mode 100644
index 0000000..7f2bbbf
--- /dev/null
+++ b/app/models/fluentd/agent/td_agent.rb
@@ -0,0 +1,29 @@
+class Fluentd
+ class Agent
+ class TdAgent
+ include Common
+
+ def self.default_options
+ {
+ :pid_file => "/var/run/td-agent/td-agent.pid",
+ :log_file => "/var/log/td-agent/td-agent.log",
+ :config_file => "/etc/td-agent/td-agent.conf",
+ }
+ end
+
+ def start
+ system('/etc/init.d/td-agent start')
+ end
+
+ def stop
+ system('/etc/init.d/td-agent stop')
+ end
+
+ def restart
+ # NOTE: td-agent has no reload command
+ # https://github.com/treasure-data/td-agent/blob/master/debian/td-agent.init#L156
+ system('/etc/init.d/td-agent restart')
+ end
+ end
+ end
+end
diff --git a/app/models/plugin.rb b/app/models/plugin.rb
index a975f5a..6a172e0 100644
--- a/app/models/plugin.rb
+++ b/app/models/plugin.rb
@@ -86,6 +86,7 @@ class Plugin
end
def self.installed
+ return [] unless File.exist?(gemfile_path)
File.read(gemfile_path).scan(/"(.*?)", "(.*?)"/).map do |plugin|
new(gem_name: plugin[0], version: plugin[1])
end
diff --git a/app/views/fluentd/_form.html.haml b/app/views/fluentd/_form.html.haml
new file mode 100644
index 0000000..b266e65
--- /dev/null
+++ b/app/views/fluentd/_form.html.haml
@@ -0,0 +1,18 @@
+%div.col-lg-6
+ - @fluentd.errors.full_messages.each do |e|
+ %div.alert.alert-danger= e
+
+ = form_for(:fluentd, url: url, method: method) do |f|
+ %div.form-group
+ = f.label :variant
+ = f.select :variant, Fluentd.variants
+ %div.form-group
+ = f.label :pid_file
+ = f.text_field :pid_file, class: "form-control"
+ %div.form-group
+ = f.label :log_file
+ = f.text_field :log_file, class: "form-control"
+ %div.form-group
+ = f.label :config_file
+ = f.text_field :config_file, class: "form-control"
+ = f.submit btn, class: "btn btn-primary"
diff --git a/app/views/fluentd/agents/show.html.haml b/app/views/fluentd/agents/show.html.haml
new file mode 100644
index 0000000..806cb1d
--- /dev/null
+++ b/app/views/fluentd/agents/show.html.haml
@@ -0,0 +1,13 @@
+- page_title t('.page_title', label: "##{@fluentd.id}")
+
+- if flash[:error]
+ %div.alert.alert-danger= flash[:error]
+
+%h4
+ = @fluentd.agent.running? ? t(".running") : t(".stopped")
+
+= link_to t(".start"), start_fluentd_agent_path(@fluentd), method: :put
+= link_to t(".stop"), stop_fluentd_agent_path(@fluentd), method: :put
+= link_to t(".restart"), restart_fluentd_agent_path(@fluentd), method: :put
+= link_to t(".log"), log_fluentd_agent_path(@fluentd)
+TODO: config
diff --git a/app/views/fluentd/daemons/show.html.haml b/app/views/fluentd/daemons/show.html.haml
deleted file mode 100644
index 9542817..0000000
--- a/app/views/fluentd/daemons/show.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-%h1
- = @fluentd.running? ? "running" : "stopped"
-
- = link_to "start", start_fluentd_daemon_path(id: 1), method: :put, remote: true
- = link_to "stop", stop_fluentd_daemon_path(id: 1), method: :put, remote: true
- = link_to "reload", reload_fluentd_daemon_path(id: 1), method: :put, remote: true
- = link_to "log", log_fluentd_daemon_path(id: 1)
diff --git a/app/views/fluentd/edit.html.haml b/app/views/fluentd/edit.html.haml
new file mode 100644
index 0000000..3a1facb
--- /dev/null
+++ b/app/views/fluentd/edit.html.haml
@@ -0,0 +1,3 @@
+- page_title t('.page_title')
+
+= render partial: "form", locals: { btn: t(".update"), url: fluentd_path(@fluentd), method: :patch }
diff --git a/app/views/fluentd/index.html.haml b/app/views/fluentd/index.html.haml
index 4c6c2ec..45d7517 100644
--- a/app/views/fluentd/index.html.haml
+++ b/app/views/fluentd/index.html.haml
@@ -1,2 +1,14 @@
-- @daemons.each do |d|
- = link_to d, fluentd_daemon_path(fluentd_id: 1) # TODO
+- page_title t('.page_title')
+
+%p= link_to icon('fa-plus') << " " << t(".new"), new_fluentd_path
+
+- @fluentds.each do |d|
+ %div.col-lg-6
+ %div.panel.panel-default
+ %div.panel-heading
+ %h4
+ = "fluentd ##{d.id}"
+ = link_to t(".edit"), edit_fluentd_path(d)
+ = link_to t(".destroy"), fluentd_path(d), method: :delete
+ %div.panel-body
+ = link_to t(".operation"), fluentd_agent_path(d) # TODO
diff --git a/app/views/fluentd/new.html.haml b/app/views/fluentd/new.html.haml
new file mode 100644
index 0000000..95c4d3a
--- /dev/null
+++ b/app/views/fluentd/new.html.haml
@@ -0,0 +1,3 @@
+- page_title t('.page_title')
+
+= render partial: "form", locals: { btn: t(".create"), url: fluentd_index_path, method: :post }
diff --git a/app/views/shared/_global_nav.html.erb b/app/views/shared/_global_nav.html.erb
index fb03829..f68fab9 100644
--- a/app/views/shared/_global_nav.html.erb
+++ b/app/views/shared/_global_nav.html.erb
@@ -2,6 +2,14 @@
<%= link_to_other icon("fa-dashboard fa-fw") << " Dashboard", root_path %>
+
+ <%= link_to_other icon("fa-puzzle-piece fa-fw") << " fluentd", fluentd_index_path %>
+
+ -
+ <%= link_to_other t("terms.new"), new_fluentd_path %>
+
+
+
<%= link_to_other icon("fa-cogs fa-fw") << " " << t('terms.plugins') << icon('fa pull-right fa-caret-down'), plugins_path %>
diff --git a/config/application.rb b/config/application.rb
index 39cfad9..3af0a38 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -13,6 +13,7 @@ require "sprockets/railtie"
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
# these gems are not required by Bundler.require
+require "sass"
require "haml-rails"
require "jquery-rails"
require "sucker_punch"
diff --git a/config/locales/translation_ja.yml b/config/locales/translation_ja.yml
index 40fbffd..ae807e4 100644
--- a/config/locales/translation_ja.yml
+++ b/config/locales/translation_ja.yml
@@ -16,6 +16,11 @@ ja:
fluent_version: "fluentd %{version}"
no_alert: なし
update_password: パスワード更新
+ create: 作成
+ update: 更新
+ edit: 編集
+ destroy: 削除
+ new: 新規作成
plugins:
common: &plugin_common
@@ -45,6 +50,32 @@ ja:
<<: *terms
page_title: ユーザー管理
+ fluentd: &fluentd
+ common: &fluentd_common
+ <<: *terms
+ start: 開始
+ stop: 停止
+ restart: 再起動
+ log: ログ
+ stopped: 停止中
+ running: 稼働中
+ operation: プロセス詳細
+ form:
+ <<: *fluentd_common
+ index:
+ <<: *fluentd_common
+ page_title: fluentd
+ new:
+ <<: *fluentd_common
+ page_title: fluentd | 追加
+ edit:
+ <<: *fluentd_common
+ page_title: fluentd | 編集
+ agents:
+ show:
+ <<: *fluentd_common
+ page_title: "fluentd %{label}"
+
misc:
common: &misc_common
<<: *terms
@@ -61,11 +92,17 @@ ja:
error:
login_failed: ログインに失敗しました。
+ fluentd_start_failed: fluentdの起動に失敗しました。
+ fluentd_stop_failed: fluentdの停止に失敗しました。
+ fluentd_restart_failed: fluentdの再起動に失敗しました。
activerecord:
errors:
messages:
wrong_password: が違います
+ lack_read_permission: の読み込み権限がありません
+ lack_write_permission: の書き込み権限がありません
+ is_a_directory: はディレクトリです。ファイルを指定してください
models:
user: user #g
@@ -78,3 +115,8 @@ ja:
current_password: 現在のパスワード
password: パスワード
password_confirmation: パスワード(確認)
+ fluentd:
+ log_file: ログファイル
+ pid_file: PIDファイル
+ config_file: 設定ファイル
+ variant: タイプ
diff --git a/config/routes.rb b/config/routes.rb
index b41c889..bc2d9ac 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,11 +1,11 @@
Rails.application.routes.draw do
root "fluentd#index" # TODO: change to dashboard
- resources :fluentd, only: [:index] do
- resource :daemon, only: [:show], module: :fluentd do
+ resources :fluentd do
+ resource :agent, only: [:show], module: :fluentd do
put "start"
put "stop"
- put "reload"
+ put "restart"
get "log"
end
resource :setting, only: [:show, :edit, :update], module: :fluentd do
diff --git a/db/migrate/20140522055753_create_fluentds.rb b/db/migrate/20140522055753_create_fluentds.rb
new file mode 100644
index 0000000..7c46301
--- /dev/null
+++ b/db/migrate/20140522055753_create_fluentds.rb
@@ -0,0 +1,12 @@
+class CreateFluentds < ActiveRecord::Migration
+ def change
+ create_table :fluentds do |t|
+ t.string :variant, null: false # fluentd, td-agent, or remote
+ t.string :pid_file
+ t.string :log_file
+ t.string :config_file
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index bbe40f2..a8b2ba2 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,16 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20140522023140) do
+ActiveRecord::Schema.define(version: 20140522055753) do
+
+ create_table "fluentds", force: true do |t|
+ t.string "variant", null: false
+ t.string "pid_file"
+ t.string "log_file"
+ t.string "config_file"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
create_table "login_tokens", force: true do |t|
t.string "token_id", null: false
diff --git a/fluentd-ui.gemspec b/fluentd-ui.gemspec
index 5d63c53..d37dab8 100644
--- a/fluentd-ui.gemspec
+++ b/fluentd-ui.gemspec
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
- spec.add_dependency "fluentd", "0.10.46"
+ spec.add_dependency "fluentd", "~> 0.10.48"
spec.add_dependency 'rails', '4.1.1'
spec.add_dependency 'sucker_punch', "~> 1.0.5"
spec.add_dependency 'i18n_generators', '1.2.1'
diff --git a/spec/factories/fluentd.rb b/spec/factories/fluentd.rb
new file mode 100644
index 0000000..5ec7b8f
--- /dev/null
+++ b/spec/factories/fluentd.rb
@@ -0,0 +1,8 @@
+FactoryGirl.define do
+ factory :fluentd do
+ variant "fluentd"
+ log_file (Rails.root + "tmp/fluentd-test/fluentd.log").to_s
+ pid_file (Rails.root + "tmp/fluentd-test/fluentd.pid").to_s
+ config_file (Rails.root + "tmp/fluentd-test/fluentd.conf").to_s
+ end
+end
diff --git a/spec/helpers/miscs_helper_spec.rb b/spec/helpers/miscs_helper_spec.rb
deleted file mode 100644
index 1b87fd3..0000000
--- a/spec/helpers/miscs_helper_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require 'spec_helper'
-
-# Specs in this file have access to a helper object that includes
-# the MiscsHelper. For example:
-#
-# describe MiscsHelper do
-# describe "string concat" do
-# it "concats two strings with spaces" do
-# expect(helper.concat_strings("this","that")).to eq("this that")
-# end
-# end
-# end
-describe MiscsHelper do
- pending "add some examples to (or delete) #{__FILE__}"
-end
diff --git a/spec/models/fluentd/agent_spec.rb b/spec/models/fluentd/agent_spec.rb
new file mode 100644
index 0000000..66a6cef
--- /dev/null
+++ b/spec/models/fluentd/agent_spec.rb
@@ -0,0 +1,119 @@
+require 'spec_helper'
+
+describe Fluentd::Agent do
+ shared_examples_for "Agent has common behavior" do |klass|
+ describe "#extra_options" do
+ context "blank" do
+ let(:options) { {} }
+ it { instance.pid_file.should == described_class.default_options[:pid_file] }
+ it { instance.log_file.should == described_class.default_options[:log_file] }
+ it { instance.config_file.should == described_class.default_options[:config_file] }
+ end
+
+ context "given" do
+ let(:options) do
+ {
+ :pid_file => pid_file,
+ :log_file => log_file,
+ :config_file => config_file,
+ }
+ end
+ let(:pid_file) { "pid" }
+ let(:log_file) { "log" }
+ let(:config_file) { "config" }
+
+ it { instance.pid_file.should == pid_file }
+ it { instance.log_file.should == log_file }
+ it { instance.config_file.should == config_file }
+ end
+ end
+ end
+
+ let(:instance) { described_class.new(options) }
+ let(:options) { {} }
+
+ describe "Fluentd" do
+ let(:described_class) { Fluentd::Agent::Fluentd } # override nested described_class behavior as https://github.com/rspec/rspec-core/issues/1114
+
+ it_should_behave_like "Agent has common behavior"
+
+ describe "#options_to_argv" do
+ subject { instance.options_to_argv }
+ it { should include("-c #{instance.config_file}") }
+ it { should include("-d #{instance.pid_file}") }
+ it { should include("-o #{instance.log_file}") }
+ it { should include("--use-v1-config") }
+ end
+
+ describe "#start" do
+ before { instance.stub(:running?).and_return { running } }
+
+ context "running" do
+ let(:running) { true }
+ after { instance.start }
+
+ it { instance.should_not_receive(:validate_fluentd_options) }
+ end
+
+ context "not running" do
+ let(:running) { false }
+ after { instance.start }
+
+ it { instance.should_receive(:validate_fluentd_options) }
+
+ context "validate_fluentd_options success" do
+ before { instance.stub(:validate_fluentd_options).and_return { true } }
+ it { instance.should_receive(:actual_start) }
+ end
+
+ context "validate_fluentd_options fail" do
+ before { instance.stub(:validate_fluentd_options).and_return { false } }
+ it { instance.should_not_receive(:actual_start) }
+ end
+ end
+ end
+
+ describe "#stop" do
+ before { instance.stub(:running?).and_return { running } }
+
+ context "running" do
+ let(:running) { true }
+ after { instance.stop }
+
+ it { instance.should_receive(:actual_stop) }
+ end
+
+ context "not running" do
+ let(:running) { false }
+ after { instance.stop }
+
+ it { instance.should_not_receive(:actual_stop) }
+ end
+ end
+
+ describe "#restart" do
+ before { instance.stub(:running?).and_return { running } }
+
+ context "running" do
+ let(:running) { true }
+ after { instance.restart }
+
+ it { instance.should_receive(:actual_restart) }
+ end
+
+ context "not running" do
+ let(:running) { false }
+ after { instance.restart }
+
+ it { instance.should_not_receive(:actual_restart) }
+ end
+ end
+ end
+
+ describe "TdAgent" do
+ let(:described_class) { Fluentd::Agent::TdAgent } # override nested described_class behavior as https://github.com/rspec/rspec-core/issues/1114
+
+ it_should_behave_like "Agent has common behavior"
+ end
+end
+
diff --git a/spec/models/fluentd_spec.rb b/spec/models/fluentd_spec.rb
new file mode 100644
index 0000000..5133ff7
--- /dev/null
+++ b/spec/models/fluentd_spec.rb
@@ -0,0 +1,127 @@
+require 'spec_helper'
+
+describe Fluentd do
+ shared_examples_for "path permission" do |column|
+ let(:path) { fluentd.send(column) }
+
+ subject do
+ fluentd.check_permission(column)
+ fluentd.errors
+ end
+
+ context "file exists" do
+ before { FileUtils.touch(path) }
+ after { FileUtils.chmod(0755, path) }
+
+ context "writable" do
+ before { FileUtils.chmod(0600, path) }
+ it { should be_blank }
+ end
+
+ context "not writable" do
+ before { FileUtils.chmod(0400, path) }
+ it { should_not be_blank }
+ it { subject.get(column).should include(I18n.t('activerecord.errors.messages.lack_write_permission')) }
+ end
+
+ context "not readable" do
+ before { FileUtils.chmod(0200, path) }
+ it { should_not be_blank }
+ it { subject.get(column).should include(I18n.t('activerecord.errors.messages.lack_read_permission')) }
+ end
+
+ context "is directory" do
+ before { fluentd.send("#{column}=", Rails.root + "tmp") }
+ it { should_not be_blank }
+ it { subject.get(column).should include(I18n.t('activerecord.errors.messages.is_a_directory')) }
+ end
+ end
+
+ context "file not exists" do
+ let(:dir) { File.dirname(path) }
+ before { FileUtils.rm path }
+ after { FileUtils.chmod_R(0755, dir) }
+
+ context "writable" do
+ before { FileUtils.chmod(0700, dir) }
+ it { should be_blank }
+ end
+
+ context "not writable" do
+ before { FileUtils.chmod(0500, dir) }
+ it { should_not be_blank }
+ it { subject.get(column).should include(I18n.t('activerecord.errors.messages.lack_write_permission')) }
+ end
+ end
+ end
+
+ let(:fluentd) { FactoryGirl.build(:fluentd) }
+
+ describe "#valid?" do
+ before do
+ %w(pid_file log_file config_file).each do |column|
+ FileUtils.mkdir_p File.dirname(fluentd.send(column))
+ FileUtils.touch fluentd.send(column)
+ end
+ end
+
+ subject { fluentd }
+
+ describe "variant" do
+ before { fluentd.variant = variant }
+
+ context "fluentd" do
+ let(:variant) { "fluentd" }
+ it { should be_valid }
+ end
+
+ context "foobar (not declared in Fluentd.variants)" do
+ let(:variant) { "foobar" }
+ it { should_not be_valid }
+ end
+ end
+
+ describe "pid_file" do
+ it_should_behave_like "path permission", :pid_file
+ end
+
+ describe "log_file" do
+ it_should_behave_like "path permission", :log_file
+ end
+
+ describe "config_file" do
+ it_should_behave_like "path permission", :config_file
+ end
+ end
+
+ describe "variant" do
+ before { fluentd.variant = variant }
+
+ context "= fluentd" do
+ let(:variant) { "fluentd" }
+ it { fluentd.should be_fluentd }
+ it { fluentd.should_not be_td_agent }
+ end
+
+ context "= td-agent" do
+ let(:variant) { "td-agent" }
+ it { fluentd.should_not be_fluentd }
+ it { fluentd.should be_td_agent }
+ end
+ end
+
+ describe "#agent" do
+ before { fluentd.variant = variant }
+ subject { fluentd.agent }
+
+ context "fluentd" do
+ let(:variant) { "fluentd" }
+ it { should be_instance_of(Fluentd::Agent::Fluentd) }
+ end
+
+ context "td-agent" do
+ let(:variant) { "td-agent" }
+ it { should be_instance_of(Fluentd::Agent::TdAgent) }
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 4dbd29c..9bd0a3b 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -64,12 +64,4 @@ RSpec.configure do |config|
config.after(:each) do
DatabaseCleaner.clean
end
-
- config.before(:each) do
- system('sync') if ENV["CIRCLECI"] # file operations are unstable on Circle CI
- end
-
- config.after(:each) do
- system('sync') if ENV["CIRCLECI"] # file operations are unstable on Circle CI
- end
end