Class: ChefApply::Startup

Inherits:
Object
  • Object
show all
Defined in:
lib/chef_apply/startup.rb

Defined Under Namespace

Classes: ConfigPathInvalid, ConfigPathNotProvided

Constant Summary collapse

T =
ChefApply::Text.cli

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(argv) ⇒ Startup

Returns a new instance of Startup.



28
29
30
31
32
33
34
# File 'lib/chef_apply/startup.rb', line 28

def initialize(argv)
  @term_init = false
  @argv = argv.clone
  # Enable CLI output via Terminal. This comes first because other startup steps may
  # need to output to the terminal.
  init_terminal
end

Instance Attribute Details

#argvObject (readonly)

Returns the value of attribute argv.



25
26
27
# File 'lib/chef_apply/startup.rb', line 25

def argv
  @argv
end

Instance Method Details

#create_default_configObject



98
99
100
101
102
103
# File 'lib/chef_apply/startup.rb', line 98

def create_default_config
  UI::Terminal.output T.creating_config(Config.default_location)
  UI::Terminal.output ""
  FileUtils.mkdir_p(Config::WS_BASE_PATH)
  FileUtils.touch(Config.default_location)
end

#custom_config_pathObject

Look for a user-supplied config path by manually parsing the option. Note that we can’t use Mixlib::CLI for this. To ensure that ChefApply::CLI initializes with correct option defaults, we need to have configuraton loaded before initializing it.



149
150
151
152
153
154
155
156
157
158
159
# File 'lib/chef_apply/startup.rb', line 149

def custom_config_path
  argv.each_with_index do |arg, index|
    if arg == "--config-path" || arg == "-c"
      next_arg = argv[index + 1]
      raise ConfigPathNotProvided.new if next_arg.nil?
      raise ConfigPathInvalid.new(next_arg) unless File.file?(next_arg) && File.readable?(next_arg)
      return next_arg
    end
  end
  nil
end

#first_run_tasksObject



92
93
94
95
96
# File 'lib/chef_apply/startup.rb', line 92

def first_run_tasks
  return if Dir.exist?(Config::WS_BASE_PATH)
  create_default_config
  setup_telemetry
end

#init_terminalObject



88
89
90
# File 'lib/chef_apply/startup.rb', line 88

def init_terminal
  UI::Terminal.init($stdout)
end

#load_configObject



139
140
141
142
143
# File 'lib/chef_apply/startup.rb', line 139

def load_config
  path = custom_config_path
  Config.custom_location(path) unless path.nil?
  Config.load
end

#runObject



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/chef_apply/startup.rb', line 36

def run
  # Some tasks we do only once in an installation:
  first_run_tasks

  # Call this every time, so that if we add or change ~/.chef-workstation
  # directory structure, we can be sure that it exists. Even with a
  # custom configuration, the .chef-workstation directory and subdirs
  # are required.
  setup_workstation_user_directories

  # Customize behavior of Ruby and any gems around error handling
  setup_error_handling

  # Startup tasks that may change behavior based on configuration value
  # must be run after load_config
  load_config

  # Init logging using log level out of config
  setup_logging

  # Begin upload of previous session telemetry. (If telemetry is not enabled,
  # in config the uploader will clean up previous session(s) without sending)
  start_telemeter_upload

  # Launch the actual Chef Apply behavior
  start_chef_apply

# NOTE: Because these exceptions occur outside of the
#       CLI handling, they won't be tracked in telemtry.
#       We can revisit this once the pending error handling rework
#       is underway.
rescue ConfigPathInvalid => e
  UI::Terminal.output(T.error.bad_config_file(e.path))
rescue ConfigPathNotProvided
  UI::Terminal.output(T.error.missing_config_path)
rescue Mixlib::Config::UnknownConfigOptionError => e
  # Ideally we'd update the exception in mixlib to include
  # a field with the faulty value, line number, and nested context -
  # it's less fragile than depending on text parsing, which
  # is what we'll do for now.
  if e.message =~ /.*unsupported config value (.*)[.]+$/
    # TODO - levenshteinian distance to figure out
    # what they may have meant instead.
    UI::Terminal.output(T.error.invalid_config_key($1, Config.location))
  else
    # Safety net in case the error text changes from under us.
    UI::Terminal.output(T.error.unknown_config_error(e.message, Config.location))
  end
rescue Tomlrb::ParseError => e
  UI::Terminal.output(T.error.unknown_config_error(e.message, Config.location))
end

#setup_error_handlingObject



128
129
130
131
132
133
134
135
136
137
# File 'lib/chef_apply/startup.rb', line 128

def setup_error_handling
  # In Ruby 2.5+ threads print out to stdout when they raise an exception. This is an agressive
  # attempt to ensure debugging information is not lost, but in our case it is not necessary
  # because we handle all the errors ourself. So we disable this to keep output clean.
  # See https://ruby-doc.org/core-2.5.0/Thread.html#method-c-report_on_exception
  #
  # We set this globally so that it applies to all threads we create - we never want any non-UI thread
  # to render error output to the terminal.
  Thread.report_on_exception = false
end

#setup_loggingObject



161
162
163
164
165
166
167
168
169
170
# File 'lib/chef_apply/startup.rb', line 161

def setup_logging
  ChefApply::Log.setup(Config.log.location, Config.log.level.to_sym)
  ChefApply::Log.info("Initialized logger")

  ChefConfig.logger = ChefApply::Log
  # Setting the config isn't enough, we need to ensure the logger is initialized
  # or automatic initialization will still go to stdout
  Chef::Log.init(ChefApply::Log)
  Chef::Log.level = ChefApply::Log.level
end

#setup_telemetryObject



105
106
107
108
109
110
111
112
113
114
# File 'lib/chef_apply/startup.rb', line 105

def setup_telemetry
  require "securerandom"
  installation_id = SecureRandom.uuid
  File.write(Config.telemetry_installation_identifier_file, installation_id)

  # Tell the user we're anonymously tracking, give brief opt-out
  # and a link to detailed information.
  UI::Terminal.output T.telemetry_enabled(Config.location)
  UI::Terminal.output ""
end

#setup_workstation_user_directoriesObject



120
121
122
123
124
125
126
# File 'lib/chef_apply/startup.rb', line 120

def setup_workstation_user_directories
  # Note that none of  these paths are customizable in config, so
  # it's safe to do before we load config.
  FileUtils.mkdir_p(Config::WS_BASE_PATH)
  FileUtils.mkdir_p(Config.base_log_directory)
  FileUtils.mkdir_p(Config.telemetry_path)
end

#start_chef_applyObject



172
173
174
175
# File 'lib/chef_apply/startup.rb', line 172

def start_chef_apply
  require "chef_apply/cli"
  ChefApply::CLI.new(@argv).run
end

#start_telemeter_uploadObject



116
117
118
# File 'lib/chef_apply/startup.rb', line 116

def start_telemeter_upload
  ChefApply::Telemeter::Sender.start_upload_thread()
end