/// module Kubernetes { var log = Logger.get('kubernetes-watcher'); var k8sTypes = KubernetesAPI.NamespacedTypes.k8sTypes; var osTypes = KubernetesAPI.NamespacedTypes.osTypes; var self = {}; var updateFunction = () => { log.debug("Objects changed, firing listeners"); var objects = {}; _.forEach(self.getTypes(), (type:string) => { objects[type] = self.getObjects(type); }); _.forEach(self.listeners, (listener:(ObjectMap) => void) => { listener(objects); }); }; var debouncedUpdate = _.debounce(updateFunction, 75, { trailing: true }); var namespaceWatch = { selected: undefined, watch: undefined, objects: [], objectMap: {}, watches: {} }; hawtioPluginLoader.registerPreBootstrapTask({ name: 'KubernetesWatcherInit', depends: ['KubernetesApiDiscovery'], task: (next) => { var booted = false; if (isOpenShift) { log.info("Backend is an Openshift instance"); } else { log.info("Backend is a vanilla Kubernetes instance"); } namespaceWatch.watch = KubernetesAPI.watch({ kind: KubernetesAPI.WatchTypes.NAMESPACES, success: (objects) => { namespaceWatch.objects = objects; if (!booted) { booted = true; self.setNamespace(localStorage[Constants.NAMESPACE_STORAGE_KEY] || defaultNamespace); next(); } log.debug("Got namespaces: ", namespaceWatch.objects); }, error: (error:any) => { log.warn("Error fetching namespaces: ", error); // TODO is this necessary? //HawtioOAuth.doLogout(); if (!booted) { booted = true; next(); } } }); } }); hawtioPluginLoader.registerPreBootstrapTask({ name: 'KubernetesApiDiscovery', depends: ['hawtio-oauth'], task: (next) => { isOpenShift = false; var userProfile = HawtioOAuth.getUserProfile(); log.debug("User profile: ", userProfile); if (userProfile && userProfile.provider === "hawtio-google-oauth") { log.debug("Possibly running on GCE"); // api master is on GCE $.ajax({ url: UrlHelpers.join(masterApiUrl(), 'api', 'v1', 'namespaces'), complete: (jqXHR, textStatus) => { if (textStatus === "success") { log.debug("jqXHR: ", jqXHR); userProfile.oldToken = userProfile.token; userProfile.token = undefined; $.ajaxSetup({ beforeSend: (request) => { } }); } next(); }, beforeSend: (request) => { } }); } else { log.debug("Not running on GCE"); // double-check if we're on vanilla k8s or openshift var rootUri = new URI(masterApiUrl()).path("/oapi").query("").toString(); log.debug("Checking for an openshift backend"); HawtioOAuth.authenticatedHttpRequest({ url: rootUri, success: (data) => { if (data) { isOpenShift = true; } next(); }, error: (jqXHR, textStatus, errorThrown) => { var error = KubernetesAPI.getErrorObject(jqXHR); if (!error) { log.debug("Failed to find root paths: ", textStatus, ": ", errorThrown); } else { log.debug("Failed to find root paths: ", error); } isOpenShift = false; next(); } }); } } }); var customUrlHandlers = {}; self.setNamespace = (namespace: string) => { if (namespace === namespaceWatch.selected) { return; } if (namespaceWatch.selected) { log.debug("Stopping current watches"); _.forOwn(namespaceWatch.watches, (watch, key) => { if (!KubernetesAPI.namespaced(key)) { return; } log.debug("Disconnecting watch: ", key); watch.disconnect(); }); _.forEach(_.keys(namespaceWatch.watches), (key) => { if (!KubernetesAPI.namespaced(key)) { return; } log.debug("Deleting kind: ", key); delete namespaceWatch.watches[key]; }); } namespaceWatch.selected = namespace; if (namespace) { _.forEach(self.getTypes(), (kind:string) => { if (kind === KubernetesAPI.WatchTypes.NAMESPACES) { return; } if (!namespaceWatch.watches[kind]) { log.debug("Creating watch for kind: ", kind); var config = { kind: kind, namespace: KubernetesAPI.namespaced(kind) ? namespace : undefined, success: (objects) => { watch.objects = objects; debouncedUpdate(); } }; if (kind in customUrlHandlers) { config.urlFunction = customUrlHandlers[kind]; } var watch = KubernetesAPI.watch(config); watch.config = config; namespaceWatch.watches[kind] = watch; } }); } }; self.hasWebSocket = true; self.getNamespace = () => namespaceWatch.selected; self.registerCustomUrlFunction = (kind:string, url:(options:KubernetesAPI.K8SOptions) => string) => { customUrlHandlers[kind] = url; if (kind in namespaceWatch.watches) { var watch = namespaceWatch.watches[kind]; var config = watch.config; config.urlFunction = url; watch.disconnect(); delete namespaceWatch.watches[kind]; config.success = (objects) => { watch.objects = objects; debouncedUpdate(); } watch = KubernetesAPI.watch(config); watch.config = config; namespaceWatch.watches[kind] = watch; } } self.getTypes = () => { var filter = (kind:string) => { // filter out stuff we don't care about yet switch(kind) { case KubernetesAPI.WatchTypes.OAUTH_CLIENTS: case KubernetesAPI.WatchTypes.IMAGE_STREAMS: case KubernetesAPI.WatchTypes.POLICIES: case KubernetesAPI.WatchTypes.ROLES: case KubernetesAPI.WatchTypes.ROLE_BINDINGS: case KubernetesAPI.WatchTypes.POLICY_BINDINGS: case KubernetesAPI.WatchTypes.PERSISTENT_VOLUME_CLAIMS: case KubernetesAPI.WatchTypes.PERSISTENT_VOLUMES: case KubernetesAPI.WatchTypes.ENDPOINTS: case KubernetesAPI.WatchTypes.RESOURCE_QUOTAS: case KubernetesAPI.WatchTypes.SERVICE_ACCOUNTS: return false; default: return true; } } var answer = k8sTypes.concat([WatchTypes.NAMESPACES]); if (isOpenShift) { answer = answer.concat(osTypes); } else { answer = answer.concat(KubernetesAPI.WatchTypes.TEMPLATES); answer = answer.concat(KubernetesAPI.WatchTypes.BUILD_CONFIGS); } return _.filter(answer, filter); } self.getObjects = (kind: string) => { if (kind === WatchTypes.NAMESPACES) { return namespaceWatch.objects; } if (kind in namespaceWatch.watches) { return namespaceWatch.watches[kind].objects; } else { return undefined; } } self.listeners = void>> []; // listener gets notified after a bunch of changes have occurred self.registerListener = (fn:(objects:ObjectMap) => void) => { self.listeners.push(fn); } var projectsHandle = undefined; // kick off the project watcher a bit sooner also hawtioPluginLoader.registerPreBootstrapTask({ name: 'ProjectsWatcher', depends: ['KubernetesApiDiscovery'], task: (next) => { if (isOpenShift) { projectsHandle = KubernetesAPI.watch({ kind: KubernetesAPI.WatchTypes.PROJECTS, namespace: undefined, success: (objects) => { if (self.listeners && self.listeners.length) { log.debug("got projects: ", objects); _.forEach(self.listeners, (listener:(objects:ObjectMap) => void) => { listener({ projects: objects }); }); } } }); } next(); } }); _module.service('WatcherService', ['userDetails', '$rootScope', '$timeout', (userDetails, $rootScope, $timeout) => { return self; }]); }