diff --git a/CHANGELOG.md b/CHANGELOG.md index f9ab800..e5d0fcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# v0.5.2 + +* add: local package index.html management +* upd: installer to exit when NAD install detected, with pointer to agent repo manual instructions +* fix: ensure streamtags are enabled in agent before running registration + # v0.5.1 * fix: option names in config file diff --git a/content/files/cosi-install.sh b/content/files/cosi-install.sh index bc8f6c6..2e702e6 100755 --- a/content/files/cosi-install.sh +++ b/content/files/cosi-install.sh @@ -916,8 +916,7 @@ cosi_initialize() { hn=$(hostname) if [[ -z "${hn:-}" && -z "${cosi_host_target:-}" ]]; then - echo "cosi requires system hostname to be set or overridden with --target" - exit 1 + fatal "cosi requires system hostname to be set or overridden with --target" fi # @@ -985,6 +984,8 @@ cosi_register() { local cosi_register_cmd="register" local cosi_register_opt="" local install_reverse="${cosi_dir}/bin/reverse_install.sh" + local agent_config="$agent_dir/etc/circonus-agent.yaml" + local agent_bin="${agent_dir}/sbin/circonus-agentd" echo __fetch_cosi_tool @@ -999,6 +1000,13 @@ cosi_register() { [[ -x "$cosi_script" ]] || fail "Unable to find cosi command '${cosi_script}'" + echo + log "### Creating base circonus-agent configuration" + echo + $agent_bin --check-metric-streamtags --check-tags="os:${cosi_os_type},arch:${cosi_os_arch},distro:${cosi_os_dist}-${cosi_os_vers}" --show-config=yaml > $agent_config + [[ $? -eq 0 ]] || fail "Error creating base circonus-agent configuration" + __restart_agent + echo log "### Running COSI registration ###" echo @@ -1006,17 +1014,13 @@ cosi_register() { "$cosi_script" "$cosi_register_cmd" | tee -a $cosi_install_log [[ ${PIPESTATUS[0]} -eq 0 ]] || fail "Errors encountered during registration." - if [[ "${cosi_agent_mode}" == "reverse" ]]; then echo log "### Enabling ${cosi_agent_mode} mode for agent ###" echo - local agent_bin="${agent_dir}/sbin/circonus-agentd" - if [[ -x $agent_bin ]]; then - $agent_bin --reverse --check-id="cosi" --api-key="cosi" --api-app="cosi" --check-metric-streamtags --check-tags="os:${cosi_os_type},arch:${cosi_os_arch},distro:${cosi_os_dist}-${cosi_os_vers}" --show-config=yaml > $agent_dir/etc/circonus-agent.yaml - [[ $? -eq 0 ]] || fail "Error updating circonus-agent configuration" - __restart_agent - fi + $agent_bin --reverse --check-id="cosi" --api-key="cosi" --api-app="cosi" --show-config=yaml --check-metric-streamtags --check-tags="os:${cosi_os_type},arch:${cosi_os_arch},distro:${cosi_os_dist}-${cosi_os_vers}" > $agent_config + [[ $? -eq 0 ]] || fail "Error updating circonus-agent configuration" + __restart_agent fi echo @@ -1049,6 +1053,19 @@ cosi_register() { cosi_install() { cosi_initialize "$@" + # + # short circuit if NAD installation detected + # + if [[ -d "${base_dir}/nad" || -f "${base_dir}/sbin/nad.js" ]]; then + echo + echo + log "*** Previous NAD installation detected ***" + log "Please use the following crconus-agent installation instructions:" + log "https://github.com/circonus-labs/circonus-agent#quick-start" + echo + echo + exit + fi cosi_verify_os cosi_check_agent cosi_register diff --git a/internal/server/local_packge_index.go b/internal/server/local_packge_index.go new file mode 100644 index 0000000..105b2b2 --- /dev/null +++ b/internal/server/local_packge_index.go @@ -0,0 +1,122 @@ +// Copyright © 2017 Circonus, Inc. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package server + +import ( + "html/template" + "io/ioutil" + "os" + "path" + "sort" + "strings" + + "github.com/pkg/errors" +) + +type Release struct { + Version string + Packages []string +} + +// updateLocalPackageIndex manages the index.html served on the +// packages/ endpoint when serving packages locally +func updateLocalPackageIndex(pkgDir string) error { + releases, err := scanPackages(pkgDir) + if err != nil { + return err + } + + if err := writeIndex(pkgDir, releases); err != nil { + return err + } + return nil +} + +func scanPackages(pkgDir string) ([]Release, error) { + if pkgDir == "" { + return nil, errors.New("invalid package path (empty)") + } + + fl, err := ioutil.ReadDir(pkgDir) + if err != nil { + return nil, err + } + + releases := []Release{} + + packageFiles := map[string][]string{} + for _, fi := range fl { + if fi.IsDir() { + continue + } + if !strings.HasPrefix(fi.Name(), "circonus-agent") { + continue + } + + parts := strings.Split(fi.Name(), "-") + if len(parts) < 3 { + continue + } + ver := parts[2] + if _, ok := packageFiles[ver]; !ok { + packageFiles[ver] = []string{} + } + packageFiles[ver] = append(packageFiles[ver], fi.Name()) + } + + verList := []string{} + for ver := range packageFiles { + verList = append(verList, ver) + } + + sort.Sort(sort.Reverse(sort.StringSlice(verList))) + + for _, ver := range verList { + releases = append(releases, Release{ + Version: ver, + Packages: packageFiles[ver], + }) + } + + return releases, nil +} + +func writeIndex(pkgDir string, releases []Release) error { + tmplDoc := ` + + + Circonus Agent Packages + +

Circonus Agent Packages

+ {{range .}} +

v{{ .Version}}

+ + {{end}} + + ` + + tmpl, err := template.New("index").Parse(tmplDoc) + if err != nil { + return err + } + + f, err := os.Create(path.Join(pkgDir, "index.html")) + if err != nil { + return err + } + + if err := tmpl.Execute(f, releases); err != nil { + f.Close() + return err + } + + return f.Close() +} diff --git a/internal/server/main.go b/internal/server/main.go index 11da67a..24e1149 100644 --- a/internal/server/main.go +++ b/internal/server/main.go @@ -27,11 +27,67 @@ import ( "github.com/circonus-labs/cosi-server/internal/templates" "github.com/justinas/alice" "github.com/pkg/errors" + "github.com/rs/zerolog" "github.com/rs/zerolog/hlog" "github.com/rs/zerolog/log" "github.com/spf13/viper" ) +// Server defines the listening servers +type Server struct { + ctx context.Context + logger zerolog.Logger + svrHTTP []*httpServer + svrHTTPS *sslServer + packageList *packages.Packages + templates *templates.Templates + info string + typerx *regexp.Regexp + distrx *regexp.Regexp + versrx *regexp.Regexp + vercleanrx *regexp.Regexp + archrx *regexp.Regexp + rhelrx *regexp.Regexp + solarisrx *regexp.Regexp + modepushrx *regexp.Regexp + modepullrx *regexp.Regexp + stats *statsd.Client + templateContentType string +} + +type httpServer struct { + address *net.TCPAddr + server *http.Server +} + +type sslServer struct { + address *net.TCPAddr + certFile string + keyFile string + server *http.Server +} + +// serverInfo is returned for a / request +type serverInfo struct { + Description string `json:"description"` + Supported []string `json:"supported"` + Version string `json:"version"` +} + +// params holds validated query parameters +type params struct { + osType string + osDistro string + osVers string + sysArch string +} + +// templateSpec holds validated template information from url path +type templateSpec struct { + Type string + Name string +} + func init() { // for random broker selection rand.Seed(time.Now().UnixNano()) @@ -80,6 +136,12 @@ func New() (*Server, error) { } s.info = string(d) + + if viper.GetBool(config.KeyLocalPackages) { + if err := updateLocalPackageIndex(viper.GetString(config.KeyLocalPackagePath)); err != nil { + return nil, errors.Wrap(err, "updating local package index") + } + } } // load templates diff --git a/internal/server/vars.go b/internal/server/vars.go deleted file mode 100644 index b3d0961..0000000 --- a/internal/server/vars.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright © 2017 Circonus, Inc. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -package server - -import ( - "context" - "net" - "net/http" - "regexp" - - "github.com/alexcesaro/statsd" - "github.com/circonus-labs/cosi-server/internal/packages" - "github.com/circonus-labs/cosi-server/internal/templates" - "github.com/rs/zerolog" -) - -// Server defines the listening servers -type Server struct { - ctx context.Context - logger zerolog.Logger - svrHTTP []*httpServer - svrHTTPS *sslServer - packageList *packages.Packages - templates *templates.Templates - info string - typerx *regexp.Regexp - distrx *regexp.Regexp - versrx *regexp.Regexp - vercleanrx *regexp.Regexp - archrx *regexp.Regexp - rhelrx *regexp.Regexp - solarisrx *regexp.Regexp - modepushrx *regexp.Regexp - modepullrx *regexp.Regexp - stats *statsd.Client - templateContentType string -} - -type httpServer struct { - address *net.TCPAddr - server *http.Server -} - -type sslServer struct { - address *net.TCPAddr - certFile string - keyFile string - server *http.Server -} - -// serverInfo is returned for a / request -type serverInfo struct { - Description string `json:"description"` - Supported []string `json:"supported"` - Version string `json:"version"` -} - -// params holds validated query parameters -type params struct { - osType string - osDistro string - osVers string - sysArch string -} - -// templateSpec holds validated template information from url path -type templateSpec struct { - Type string - Name string -}