blob: a790ddfbae20f3d3c7974d60c322d087cb06d5e1 [file] [log] [blame]
;; Licensed to the Apache Software Foundation (ASF) under one
;; or more contributor license agreements. See the NOTICE file
;; distributed with this work for additional information
;; regarding copyright ownership. The ASF licenses this file
;; to you under the Apache License, Version 2.0 (the
;; "License"); you may not use this file except in compliance
;; with the License. You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
(ns backtype.storm.logviewer-test
(:use [backtype.storm config util])
(:require [backtype.storm.daemon [logviewer :as logviewer]
[supervisor :as supervisor]])
(:require [conjure.core])
(:use [clojure test])
(:use [conjure core])
(:import [org.mockito Mockito]))
(defmulti mk-mock-File #(:type %))
(defmethod mk-mock-File :file [{file-name :name mtime :mtime
:or {file-name "afile" mtime 1}}]
(let [mockFile (Mockito/mock java.io.File)]
(. (Mockito/when (.getName mockFile)) thenReturn file-name)
(. (Mockito/when (.lastModified mockFile)) thenReturn mtime)
(. (Mockito/when (.isFile mockFile)) thenReturn true)
(. (Mockito/when (.getCanonicalPath mockFile))
thenReturn (str "/mock/canonical/path/to/" file-name))
mockFile))
(defmethod mk-mock-File :directory [{dir-name :name mtime :mtime
:or {dir-name "adir" mtime 1}}]
(let [mockDir (Mockito/mock java.io.File)]
(. (Mockito/when (.getName mockDir)) thenReturn dir-name)
(. (Mockito/when (.lastModified mockDir)) thenReturn mtime)
(. (Mockito/when (.isFile mockDir)) thenReturn false)
mockDir))
(deftest test-mk-FileFilter-for-log-cleanup
(testing "log file filter selects the correct log files for purge"
(let [now-millis (current-time-millis)
conf {LOGVIEWER-CLEANUP-AGE-MINS 60
LOGVIEWER-CLEANUP-INTERVAL-SECS 300}
cutoff-millis (logviewer/cleanup-cutoff-age-millis conf now-millis)
old-mtime-millis (- cutoff-millis 500)
new-mtime-millis (+ cutoff-millis 500)
matching-files (map #(mk-mock-File %)
[{:name "oldlog-1-2-worker-3.log"
:type :file
:mtime old-mtime-millis}
{:name "oldlog-1-2-worker-3.log.8"
:type :file
:mtime old-mtime-millis}
{:name "foobar*_topo-1-24242-worker-2834238.log"
:type :file
:mtime old-mtime-millis}])
excluded-files (map #(mk-mock-File %)
[{:name "oldlog-1-2-worker-.log"
:type :file
:mtime old-mtime-millis}
{:name "olddir-1-2-worker.log"
:type :directory
:mtime old-mtime-millis}
{:name "newlog-1-2-worker.log"
:type :file
:mtime new-mtime-millis}
{:name "some-old-file.txt"
:type :file
:mtime old-mtime-millis}
{:name "metadata"
:type :directory
:mtime old-mtime-millis}
{:name "newdir-1-2-worker.log"
:type :directory
:mtime new-mtime-millis}
{:name "newdir"
:type :directory
:mtime new-mtime-millis}
])
file-filter (logviewer/mk-FileFilter-for-log-cleanup conf now-millis)]
(is (every? #(.accept file-filter %) matching-files))
(is (not-any? #(.accept file-filter %) excluded-files))
)))
(deftest test-get-log-root->files-map
(testing "returns map of root name to list of files"
(let [files (vec (map #(java.io.File. %) ["log-1-2-worker-3.log"
"log-1-2-worker-3.log.1.gz"
"log-1-2-worker-3.log.err"
"log-1-2-worker-3.log.out"
"log-1-2-worker-3.log.out.1.gz"
"log-1-2-worker-3.log.1"
"log-2-4-worker-6.log.1"]))
expected {"log-1-2-worker-3" #{(files 0) (files 1) (files 2) (files 3) (files 4) (files 5)}
"log-2-4-worker-6" #{(files 6)}}]
(is (= expected (logviewer/get-log-root->files-map files))))))
(deftest test-identify-worker-log-files
(testing "Does not include metadata file when there are any log files that
should not be cleaned up"
(let [cutoff-millis 2000
old-logFile (mk-mock-File {:name "mock-1-1-worker-1.log.1"
:type :file
:mtime (- cutoff-millis 1000)})
mock-metaFile (mk-mock-File {:name "mock-1-1-worker-1.yaml"
:type :file
:mtime 1})
new-logFile (mk-mock-File {:name "mock-1-1-worker-1.log"
:type :file
:mtime (+ cutoff-millis 1000)})
exp-id "id12345"
exp-user "alice"
expected {exp-id {:owner exp-user
:files #{old-logFile}}}]
(stubbing [supervisor/read-worker-heartbeats nil
logviewer/get-metadata-file-for-log-root-name mock-metaFile
read-dir-contents [(.getName old-logFile) (.getName new-logFile)]
logviewer/get-worker-id-from-metadata-file exp-id
logviewer/get-topo-owner-from-metadata-file exp-user]
(is (= expected (logviewer/identify-worker-log-files [old-logFile] "/tmp/")))))))
(deftest test-get-dead-worker-files-and-owners
(testing "removes any files of workers that are still alive"
(let [conf {SUPERVISOR-WORKER-TIMEOUT-SECS 5}
id->hb {"42" {:time-secs 1}}
now-secs 2
log-files #{:expected-file :unexpected-file}
exp-owner "alice"]
(stubbing [logviewer/identify-worker-log-files {"42" {:owner exp-owner
:files #{:unexpected-file}}
"007" {:owner exp-owner
:files #{:expected-file}}}
logviewer/get-topo-owner-from-metadata-file "alice"
supervisor/read-worker-heartbeats id->hb]
(is (= [{:owner exp-owner :files #{:expected-file}}]
(logviewer/get-dead-worker-files-and-owners conf now-secs log-files "/tmp/")))))))
(deftest test-cleanup-fn
(testing "cleanup function removes file as user when one is specified"
(let [exp-user "mock-user"
mockfile1 (mk-mock-File {:name "file1" :type :file})
mockfile2 (mk-mock-File {:name "file2" :type :file})
mockfile3 (mk-mock-File {:name "file3" :type :file})
mockyaml (mk-mock-File {:name "foo.yaml" :type :file})
exp-cmd (str "rmr /mock/canonical/path/to/" (.getName mockfile3))]
(stubbing [logviewer/select-files-for-cleanup
[(mk-mock-File {:name "throwaway" :type :file})]
logviewer/get-dead-worker-files-and-owners
[{:owner nil :files #{mockfile1}}
{:files #{mockfile2}}
{:owner exp-user :files #{mockfile3 mockyaml}}]
supervisor/worker-launcher nil
rmr nil]
(logviewer/cleanup-fn! "/tmp/")
(verify-call-times-for supervisor/worker-launcher 1)
(verify-first-call-args-for-indices supervisor/worker-launcher
[1 2] exp-user exp-cmd)
(verify-call-times-for rmr 3)
(verify-nth-call-args-for 1 rmr (.getCanonicalPath mockfile1))
(verify-nth-call-args-for 2 rmr (.getCanonicalPath mockfile2))
(verify-nth-call-args-for 3 rmr (.getCanonicalPath mockyaml))))))
(deftest test-authorized-log-user
(testing "allow cluster admin"
(let [conf {NIMBUS-ADMINS ["alice"]}]
(stubbing [logviewer/get-log-user-group-whitelist [[] []]
logviewer/user-groups []]
(is (logviewer/authorized-log-user? "alice" "non-blank-fname" conf))
(verify-first-call-args-for logviewer/get-log-user-group-whitelist "non-blank-fname")
(verify-first-call-args-for logviewer/user-groups "alice"))))
(testing "ignore any cluster-set topology.users topology.groups"
(let [conf {TOPOLOGY-USERS ["alice"]
TOPOLOGY-GROUPS ["alice-group"]}]
(stubbing [logviewer/get-log-user-group-whitelist [[] []]
logviewer/user-groups ["alice-group"]]
(is (not (logviewer/authorized-log-user? "alice" "non-blank-fname" conf)))
(verify-first-call-args-for logviewer/get-log-user-group-whitelist "non-blank-fname")
(verify-first-call-args-for logviewer/user-groups "alice"))))
(testing "allow cluster logs user"
(let [conf {LOGS-USERS ["alice"]}]
(stubbing [logviewer/get-log-user-group-whitelist [[] []]
logviewer/user-groups []]
(is (logviewer/authorized-log-user? "alice" "non-blank-fname" conf))
(verify-first-call-args-for logviewer/get-log-user-group-whitelist "non-blank-fname")
(verify-first-call-args-for logviewer/user-groups "alice"))))
(testing "allow whitelisted topology user"
(stubbing [logviewer/get-log-user-group-whitelist [["alice"] []]
logviewer/user-groups []]
(is (logviewer/authorized-log-user? "alice" "non-blank-fname" {}))
(verify-first-call-args-for logviewer/get-log-user-group-whitelist "non-blank-fname")
(verify-first-call-args-for logviewer/user-groups "alice")))
(testing "allow whitelisted topology group"
(stubbing [logviewer/get-log-user-group-whitelist [[] ["alice-group"]]
logviewer/user-groups ["alice-group"]]
(is (logviewer/authorized-log-user? "alice" "non-blank-fname" {}))
(verify-first-call-args-for logviewer/get-log-user-group-whitelist "non-blank-fname")
(verify-first-call-args-for logviewer/user-groups "alice")))
(testing "disallow user not in nimbus admin, topo user, logs user, or whitelist"
(stubbing [logviewer/get-log-user-group-whitelist [[] []]
logviewer/user-groups []]
(is (not (logviewer/authorized-log-user? "alice" "non-blank-fname" {})))
(verify-first-call-args-for logviewer/get-log-user-group-whitelist "non-blank-fname")
(verify-first-call-args-for logviewer/user-groups "alice"))))