[java-identity-provider] 03/03: IDP-2114 Add metrics for installed plugins and modules

Rod Widdowson rdw at steadingsoftware.com
Tue Jun 20 14:51:29 UTC 2023


This is an automated email from the git hooks/post-receive script.

rdw pushed a commit to branch main
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=96a77deebb3fd462314c2c40924325aa90ab4775

commit 96a77deebb3fd462314c2c40924325aa90ab4775
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Tue Jun 20 15:17:34 2023 +0100

    IDP-2114 Add metrics for installed plugins and modules
    
    https://shibboleth.atlassian.net/browse/IDP-2114
    
    Guages to report
     * All Plugins' and versions
     * All Plugin supported and update states
     * IdP Update state
---
 .../admin/impl/InstallableComponentGuageSet.java   | 257 +++++++++++++++++++++
 .../shibboleth/idp}/plugin/impl/PluginInfo.java    |   4 +-
 .../net/shibboleth/idp/conf/admin-system.xml       |   6 +
 .../shibboleth/idp/module/conf/admin/metrics.xml   |   1 +
 .../installer/plugin/impl/PluginInstallerCLI.java  |   1 +
 .../idp/installer/plugin/impl/PluginState.java     |   1 +
 6 files changed, 268 insertions(+), 2 deletions(-)

diff --git a/idp-admin-impl/src/main/java/net/shibboleth/idp/admin/impl/InstallableComponentGuageSet.java b/idp-admin-impl/src/main/java/net/shibboleth/idp/admin/impl/InstallableComponentGuageSet.java
new file mode 100644
index 000000000..520e58f57
--- /dev/null
+++ b/idp-admin-impl/src/main/java/net/shibboleth/idp/admin/impl/InstallableComponentGuageSet.java
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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.
+ */
+
+package net.shibboleth.idp.admin.impl;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.ServiceLoader;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.hc.client5.http.classic.HttpClient;
+import org.jetbrains.annotations.NotNull;
+import org.opensaml.security.httpclient.HttpClientSecurityParameters;
+import org.slf4j.Logger;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.MetricSet;
+
+import net.shibboleth.idp.Version;
+import net.shibboleth.idp.plugin.IdPPlugin;
+import net.shibboleth.idp.plugin.impl.PluginInfo;
+import net.shibboleth.profile.installablecomponent.InstallableComponentInfo;
+import net.shibboleth.profile.installablecomponent.InstallableComponentInfo.VersionInfo;
+import net.shibboleth.profile.installablecomponent.InstallableComponentSupport;
+import net.shibboleth.profile.installablecomponent.InstallableComponentSupport.SupportLevel;
+import net.shibboleth.profile.installablecomponent.InstallableComponentVersion;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.NullableElements;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * Guage set to report the Plugins' & IdP installation and update statuses.
+ */
+/**
+ *
+ */
+public class InstallableComponentGuageSet extends AbstractIdentifiableInitializableComponent implements MetricSet  {
+
+    /** Logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(InstallableComponentGuageSet.class);
+
+    /** Default prefix for metrics. */
+    @Nonnull @NotEmpty private static final String DEFAULT_METRIC_NAME = "net.shibboleth.idp.installedcomponent";
+
+    /** The map of gauges. */
+    @Nonnull @NonnullElements private final Map<String,Metric> gauges = new HashMap<>();
+
+    /** Where to look for update information. */
+    @Nonnull private List<URL> idpUpdateUrls = CollectionSupport.emptyList(); 
+
+    /** How to reach out. */
+    @NonnullAfterInit private HttpClient httpClient;
+
+    /** any security parameters needed. */
+    @Nullable private HttpClientSecurityParameters securityParams;
+    
+    /** IdP Version to check. */
+    @NonnullAfterInit private InstallableComponentVersion idpVersion;
+    
+    public InstallableComponentGuageSet() {
+        gauges.put(MetricRegistry.name(DEFAULT_METRIC_NAME, "plugins", "list"),
+                new Gauge<Map<String, InstallableComponentVersion>>() {
+                    public Map<String, InstallableComponentVersion> getValue() {
+                        return getPluginList();
+                    }
+                });
+        gauges.put(MetricRegistry.name(DEFAULT_METRIC_NAME, "plugins", "details"),
+                new Gauge<Map<String, InstallableComponentDetails>>() {
+                    public Map<String, InstallableComponentDetails> getValue() {
+                        return getPluginDetails();
+                    }
+                });
+        gauges.put(MetricRegistry.name(DEFAULT_METRIC_NAME, "idp", "details"),
+                new Gauge<InstallableComponentDetails>() {
+                    public InstallableComponentDetails getValue() {
+                        return getIdPDetails();
+                    }
+                });
+
+    }
+    
+    /** Set where to look for idp update info.
+     * @param urls what to set.
+     */
+    public void setIdpUpdateUrls(@Nonnull final List<URL> urls) {
+        idpUpdateUrls =  urls;
+    }
+
+    /** Set any {@link HttpClientSecurityParameters}.
+     * @param params what to set.
+     */
+    public void setSecurityParams(@Nullable final HttpClientSecurityParameters params) {
+        securityParams = params;
+    }
+
+    /** Set the {@link HttpClient} to use.
+     * @param client what to set.
+     */
+    public void setHttpClient(@Nonnull final HttpClient client) {
+        httpClient = Constraint.isNotNull(client, "HttpClient cannot be null");
+    }
+    
+    /** Null safe IdP Version
+     * @return Returns the idPVersion.
+     */
+    @Nonnull InstallableComponentVersion getIdPVersion() {
+        checkComponentActive();
+        assert idpVersion!=null;
+        return idpVersion;
+    }
+
+    /** Return the list of installed components and their versions
+     * @return a Map where the key is the PluginId and the value is the version.
+     */
+    @Nonnull @NotLive private Map<String, InstallableComponentVersion> getPluginList() {
+        final Map<String, InstallableComponentVersion> result = new HashMap<>();
+        final Iterator<IdPPlugin> plugins = ServiceLoader.load(IdPPlugin.class).iterator();
+        while (plugins.hasNext()) {
+            final IdPPlugin plugin = plugins.next();
+            result.put(plugin.getPluginId(), new InstallableComponentVersion(plugin));
+        }
+        return CollectionSupport.copyToMap(result);
+    }
+
+    private Map<String, InstallableComponentDetails> getPluginDetails() {
+        @NullableElements final Map<URL, Properties> pluginInfoCache = new HashMap<>();
+        final Map<String, InstallableComponentDetails> result = new HashMap<>();
+        final Iterator<IdPPlugin> plugins = ServiceLoader.load(IdPPlugin.class).iterator();
+        while (plugins.hasNext()) {
+            final IdPPlugin plugin = plugins.next();
+            assert plugin!=null;
+            final Properties properties = lookupIdPProperties(plugin, pluginInfoCache);
+            if (properties==null) {
+                log.error("Could not located Plugin version information");
+                continue;
+            }
+            final InstallableComponentInfo info = new PluginInfo(plugin.getPluginId(), properties);
+            final InstallableComponentVersion pluginVersion = new InstallableComponentVersion(plugin);
+            final VersionInfo verInfo = info.getAvailableVersions().get(pluginVersion);
+            if (verInfo == null) {
+                log.error("Could not located Plugin version information");
+                continue;
+            }
+            final InstallableComponentVersion newPluginVersion = InstallableComponentSupport.getBestVersion(getIdPVersion(), pluginVersion, info);
+            result.put(plugin.getPluginId(), new InstallableComponentDetails(verInfo.getSupportLevel(), newPluginVersion));
+        }    
+        return CollectionSupport.copyToMap(result);
+    }
+
+    /** Look in the cache for the URL and return the properties if its there. 
+     * Otherwise reach out to the URP and load the properties
+     * @param plugin the {@link IdPPlugin} to consider.
+     * @param pluginInfoCache the cache of already looked up info.  Also serves as a negative cache.
+     * @return
+     */
+    @Nullable private Properties lookupIdPProperties(@NotNull final IdPPlugin plugin, @NullableElements final Map<URL, Properties> pluginInfoCache) {
+        final List<URL> urls;
+        try {
+            urls = plugin.getUpdateURLs();
+        } catch (final IOException e) {
+            log.error("Could not locate plugin {} update urls", plugin.getPluginId(), e);
+            return null;
+        }
+        for (final URL url:urls) {
+            if (pluginInfoCache.containsKey(url)) {
+                return pluginInfoCache.get(url);
+            }
+        }
+        assert httpClient != null;
+        final Properties result = InstallableComponentSupport.loadInfo(urls, httpClient, securityParams);
+        for (final URL url:urls) {
+            // Note - negative caching too.
+            pluginInfoCache.put(url, result);
+        }
+        return result;
+    }
+
+    /** Return information about the Installation update state of the IdP.
+     * @return a {@link InstallableComponentDetails}
+     */
+    @Nullable private InstallableComponentDetails getIdPDetails() {
+        try {
+            assert httpClient!=null;
+            final Properties properties = InstallableComponentSupport.loadInfo(idpUpdateUrls, httpClient, securityParams);
+            if (properties == null) {
+                log.error("Could not locate IdP update information");
+                return null;
+            }
+            final InstallableComponentInfo info = new IdPInfo(properties);
+            final VersionInfo verInfo = info.getAvailableVersions().get(getIdPVersion());
+            if (verInfo == null) {
+                log.error("Could not located IdP version information");
+                return null;
+            }
+            final InstallableComponentVersion newIdPVersion = InstallableComponentSupport.getBestVersion(getIdPVersion(), getIdPVersion(), info);
+            return new InstallableComponentDetails(verInfo.getSupportLevel(), newIdPVersion);
+        } catch (final Throwable t) {
+            log.error("Check for IdP update status failed unexpectedly", t);
+            return null;
+        }
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        if (httpClient == null) {
+            throw new ComponentInitializationException("Http Client was null");
+        }
+        final String idpVersionStr = Version.getVersion();
+        if (idpVersionStr == null) {
+            // So things work inside eclipse.
+            log.error("Could not find Current IdP Version, assuming V5.0.0");
+            idpVersion = new InstallableComponentVersion(5,0,0);
+        } else {
+            idpVersion = new InstallableComponentVersion(idpVersionStr); 
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Map<String, Metric> getMetrics() {
+        return gauges;
+    }
+
+    record InstallableComponentDetails(@Nonnull @NotEmpty SupportLevel supportedState, @Nullable InstallableComponentVersion updateVersion) { };
+}
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInfo.java b/idp-admin-impl/src/main/java/net/shibboleth/idp/plugin/impl/PluginInfo.java
similarity index 96%
rename from idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInfo.java
rename to idp-admin-impl/src/main/java/net/shibboleth/idp/plugin/impl/PluginInfo.java
index fd418fa86..4524f336e 100644
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInfo.java
+++ b/idp-admin-impl/src/main/java/net/shibboleth/idp/plugin/impl/PluginInfo.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.installer.plugin.impl;
+package net.shibboleth.idp.plugin.impl;
 
 import java.util.Properties;
 
@@ -28,7 +28,7 @@ import net.shibboleth.profile.installablecomponent.InstallableComponentVersion;
 import net.shibboleth.shared.primitive.StringSupport;
 
 /**
- * Information about a Plugin
+ * Information about a Plugin.
  */
 public class PluginInfo extends InstallableComponentInfo {
 
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/admin-system.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/admin-system.xml
index 17d349ac9..95f271e45 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/admin-system.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/admin-system.xml
@@ -340,6 +340,12 @@
         c:metricName="net.shibboleth.idp.managedbean"
         p:service-ref="shibboleth.ManagedBeanService" />
 
+    <bean id="shibboleth.metrics.InstallableComponents"
+        class="net.shibboleth.idp.admin.impl.InstallableComponentGuageSet" lazy-init="true"
+       p:idpUpdateUrls-ref="%{idp.updateCheck.urls:shibboleth.IdPUpdateCheckUrls}"
+       p:httpClient-ref="%{idp.updateCheck.httpClient:shibboleth.InternalHttpClient}"
+       p:securityParams="#{ environment.containsProperty('idp.updateCheck.httpSecurityParameters') ? getObject('idp.updateCheck.httpSecurityParameters') :null}"/>
+
     <util:list id="shibboleth.IdPUpdateCheckUrls">
         <value>https://shibboleth.net/downloads/identity-provider/plugins/idp-versions.properties</value>
         <value>http://plugins.shibboleth.net/idp-versions.properties</value>
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/admin/metrics.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/admin/metrics.xml
index 782d707a3..ea6bb25f4 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/admin/metrics.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/admin/metrics.xml
@@ -32,6 +32,7 @@
                 <ref bean="shibboleth.metrics.CASServiceRegistryGaugeSet" />
                 <ref bean="shibboleth.metrics.ManagedBeanGaugeSet" />
                 <ref bean="shibboleth.metrics.ModuleGaugeSet" />
+                <ref bean="shibboleth.metrics.InstallableComponents" />
 
                 <!--
                 <bean class="com.codahale.metrics.jvm.CachedThreadStatesGaugeSet"
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerCLI.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerCLI.java
index fd30ac38e..75bc7d4de 100644
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerCLI.java
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerCLI.java
@@ -48,6 +48,7 @@ import net.shibboleth.idp.Version;
 import net.shibboleth.idp.cli.AbstractIdPHomeAwareCommandLine;
 import net.shibboleth.idp.installer.InstallerSupport;
 import net.shibboleth.idp.plugin.IdPPlugin;
+import net.shibboleth.idp.plugin.impl.PluginInfo;
 import net.shibboleth.profile.installablecomponent.InstallableComponentInfo;
 import net.shibboleth.profile.installablecomponent.InstallableComponentSupport;
 import net.shibboleth.profile.installablecomponent.InstallableComponentVersion;
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginState.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginState.java
index a03dd3248..f6d7a2b1a 100644
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginState.java
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginState.java
@@ -33,6 +33,7 @@ import org.springframework.core.io.FileSystemResource;
 import org.springframework.core.io.Resource;
 
 import net.shibboleth.idp.plugin.IdPPlugin;
+import net.shibboleth.idp.plugin.impl.PluginInfo;
 import net.shibboleth.profile.installablecomponent.InstallableComponentInfo;
 import net.shibboleth.profile.installablecomponent.InstallableComponentVersion;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list