Project

General

Profile

« Previous | Next » 

Revision d09c18c1

Added by François ARMAND over 6 years ago

Fixes #11429: Update datasource to Rudder 4.2 license framework

View differences:

pom.xml
<parent>
<groupId>com.normation.rudder</groupId>
<artifactId>rudder-parent</artifactId>
<version>4.1.0</version>
<version>4.2.0~rc3-SNAPSHOT</version>
</parent>
<description>
src/main/scala-templates/default/com/normation/plugins/datasources/EnablePluginImpl.scala
package com.normation.plugins.datasources
import bootstrap.rudder.plugin.CheckRudderPluginDatasourcesEnable
import bootstrap.rudder.plugin.DatasourcesStatus
import com.normation.plugins.PluginStatus
import com.normation.plugins.PluginStatusInfo
/*
* The class will be loaded by ServiceLoader, it needs an empty constructor.
* unlimited version of the plugin
*/
final class CheckRudderPluginDatasourcesEnableImpl() extends CheckRudderPluginDatasourcesEnable {
val hasLicense = false
val isEnabled = true
val enabledStatus = DatasourcesStatus.Enabled
val licenseInformation = None
final class CheckRudderPluginDatasourcesEnableImpl() extends PluginStatus {
override val isEnabled = true
override val enabledStatus = PluginStatusInfo.EnabledNoLicense
}
src/main/scala-templates/limited/com/normation/plugins/datasources/EnablePluginImpl.scala
import java.util.Formatter.DateTime
import java.io.InputStreamReader
import org.joda.time.DateTime
import bootstrap.rudder.plugin.CheckRudderPluginDatasourcesEnable
import bootstrap.rudder.plugin.DatasourcesStatus
import bootstrap.rudder.plugin.DatasourcesLicenseInfo
import com.normation.plugins.PluginStatus
import com.normation.plugins.PluginStatusInfo
import com.normation.plugins.PluginLicenseInfo
/*
......
* The class will be loaded by ServiceLoader, it needs an empty constructor.
*/
final class CheckRudderPluginDatasourcesEnableImpl() extends CheckRudderPluginDatasourcesEnable {
final class CheckRudderPluginDatasourcesEnableImpl() extends PluginStatus {
// here are processed variables
val CLASSPATH_KEYFILE = "${plugin-resource-publickey}"
val FS_SIGNED_LICENSE = "${plugin-resource-license}"
val VERSION = "${plugin-declared-version}"
val maybeLicense = LicenseReader.readLicense(FS_SIGNED_LICENSE)
val hasLicense = true
// for now, we only read license info at load, because it's time consuming
val maybeInfo =
val maybeInfo = {
for {
unchecked <- maybeLicense
publicKey <- {
val key = this.getClass.getClassLoader.getResourceAsStream(CLASSPATH_KEYFILE)
if(key == null) {
Left(LicenseError.IO(s"The resources '${CLASSPATH_KEYFILE}' was not found"))
Left(LicenseError.IO(s"The classpath resources '${CLASSPATH_KEYFILE}' was not found"))
} else {
RSAKeyManagement.readPKCS8PublicKey(new InputStreamReader(key), None) //don't give to much info about path
}
......
} yield {
(checked, version)
}
}
maybeInfo.fold( error => DataSourceLogger.error(error) , ok => DataSourceLogger.warn("License signature is valid.") )
//log at that point is we read the license information for the plugin
maybeInfo.fold( error => DataSourceLogger.error(error) , ok => DataSourceLogger.info("Plugin 'datasources' has a license and the license signature is valid.") )
def isEnabled = enabledStatus == DatasourcesStatus.Enabled
def enabledStatus: DatasourcesStatus = {
def current: PluginStatusInfo = {
(for {
info <- maybeInfo
(license, version) = info
......
} yield {
check
}) match {
case Right(x) => DatasourcesStatus.Enabled
case Left (y) => DatasourcesStatus.Disabled(y.msg)
case Right(x) => PluginStatusInfo.EnabledWithLicense(licenseInformation(x))
case Left (y) => PluginStatusInfo.Disabled(y.msg, maybeLicense.toOption.map(licenseInformation))
}
}
def licenseInformation(): Option[DatasourcesLicenseInfo] = maybeLicense match {
case Left(_) => None
case Right(l) => Some(DatasourcesLicenseInfo(
licensee = l.content.licensee.value
, softwareId = l.content.softwareId.value
, minVersion = l.content.minVersion.value.toString
, maxVersion = l.content.maxVersion.value.toString
, startDate = l.content.startDate.value
, endDate = l.content.endDate.value
))
private[this] def licenseInformation(l: License): PluginLicenseInfo = {
PluginLicenseInfo(
licensee = l.content.licensee.value
, softwareId = l.content.softwareId.value
, minVersion = l.content.minVersion.value.toString
, maxVersion = l.content.maxVersion.value.toString
, startDate = l.content.startDate.value
, endDate = l.content.endDate.value
)
}
}
src/main/scala/bootstrap/rudder/plugin/EnablePlugin.scala
/*
*************************************************************************************
* Copyright 2017 Normation SAS
*************************************************************************************
*
* This file is part of Rudder.
*
* Rudder is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In accordance with the terms of section 7 (7. Additional Terms.) of
* the GNU General Public License version 3, the copyright holders add
* the following Additional permissions:
* Notwithstanding to the terms of section 5 (5. Conveying Modified Source
* Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
* Public License version 3, when you create a Related Module, this
* Related Module is not considered as a part of the work and may be
* distributed under the license agreement of your choice.
* A "Related Module" means a set of sources files including their
* documentation that, without modification of the Source Code, enables
* supplementary functions or services in addition to those offered by
* the Software.
*
* Rudder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Rudder. If not, see <http://www.gnu.org/licenses/>.
*
*************************************************************************************
*/
package bootstrap.rudder.plugin
import org.joda.time.DateTime
/**
* This file defined an entry point for license information and other
* enabling of the plugin
*/
sealed trait DatasourcesStatus
final object DatasourcesStatus {
final case object Enabled extends DatasourcesStatus
final case class Disabled(reason: String) extends DatasourcesStatus
}
/*
* This object gives main information about license information.
* It is destinated to be read to the user. No string information
* should be used for comparison.
*/
final case class DatasourcesLicenseInfo(
licensee : String
, softwareId: String
, minVersion: String
, maxVersion: String
, startDate : DateTime
, endDate : DateTime
)
trait CheckRudderPluginDatasourcesEnable {
/*
* Does the plugin has a license to display?
*/
def hasLicense(): Boolean
/*
* Is the plugin currently enabled (at the moment of the request)
*/
def isEnabled(): Boolean
/*
* What is the CURRENT status of the plugin at the moment of the request
*/
def enabledStatus(): DatasourcesStatus
/*
* Information about the license. Maybe none is the plugin
* is not a limited version.
*/
def licenseInformation(): Option[DatasourcesLicenseInfo]
}
src/main/scala/com/normation/plugins/datasources/DataSourcesPluginDef.scala
import net.liftweb.sitemap.Menu
import scala.xml.NodeSeq
import com.typesafe.config.ConfigFactory
import bootstrap.rudder.plugin.CheckRudderPluginDatasourcesEnable
import bootstrap.rudder.plugin.DatasourcesStatus
import com.normation.plugins.PluginStatus
class DataSourcesPluginDef(info: CheckRudderPluginDatasourcesEnable) extends RudderPluginDef with Loggable {
class DataSourcesPluginDef(info: PluginStatus) extends RudderPluginDef with Loggable {
override val basePackage = "com.normation.plugins.datasources"
......
}
override def description : NodeSeq = (
<div>
<p>
Data source plugin allows to get node properties from third parties provider accessible via a REST API.
Configuration can be done in <a href="/secure/administration/dataSourceManagement">the dedicated management page</a>
</p>
{ // license information
if(!info.hasLicense) {
NodeSeq.Empty
} else {
val (bg, msg) = info.enabledStatus() match {
case DatasourcesStatus.Enabled => ("info", None)
case DatasourcesStatus.Disabled(msg) => ("danger", Some(msg))
}
<div class="tw-bs"><div id="license-information" style="padding:5px; margin: 5px;" class={s"bs-callout bs-callout-${bg}"}>
<h4>License information</h4>
<p>This binary version of the plugin is submited to a license with the
following information:</p>
<div class={s"bg-${bg}"}>{
info.licenseInformation() match {
case None => <p>It was not possible to read information about the license.</p>
case Some(i) =>
<dl class="dl-horizontal" style="padding:5px;">
<dt>Plugin with ID</dt> <dd>{i.softwareId}</dd>
<dt>Licensee</dt> <dd>{i.licensee}</dd>
<dt>Supported version</dt> <dd>from {i.minVersion} to {i.maxVersion}</dd>
<dt>Validity period</dt> <dd>from {i.startDate.toString("YYYY-MM-dd")} to {i.endDate.toString("YYYY-MM-dd")}</dd>
</dl>
}}
{msg.map(s => <p class="text-danger">{s}</p>).getOrElse(NodeSeq.Empty)}
</div>
</div></div>
}
}
</div>
)
val status: PluginStatus = info
def init = {
PluginLogger.info(s"loading '${buildConf.getString("plugin-id")}:${version.toString}' plugin")
LiftRules.statelessDispatch.append(DatasourcesConf.dataSourceApi9)
src/main/scala/com/normation/plugins/datasources/Repository.scala
import scala.concurrent.duration._
import scalaz.{ Failure => _ }
import scalaz.Scalaz._
import bootstrap.rudder.plugin.CheckRudderPluginDatasourcesEnable
import com.normation.plugins.PluginStatus
final case class PartialNodeUpdate(
nodes : Map[NodeId, NodeInfo] //the node to update
......
backend : DataSourceRepository
, fetch : QueryDataSourceService
, uuidGen : StringUuidGenerator
, pluginStatus: CheckRudderPluginDatasourcesEnable
, pluginStatus: PluginStatus
) extends DataSourceRepository with DataSourceUpdateCallbacks {
/*
src/main/scala/com/normation/plugins/datasources/Scheduler.scala
import net.liftweb.common.Loggable
import scala.concurrent.duration.FiniteDuration
import scala.util.control.NonFatal
import bootstrap.rudder.plugin.CheckRudderPluginDatasourcesEnable
import com.normation.plugins.PluginStatus
final case class UpdateCause(modId: ModificationId, actor:EventActor, reason:Option[String], triggeredByGeneration: Boolean = false)
......
class DataSourceScheduler(
val datasource : DataSource
, implicit val scheduler : Scheduler
, pluginStatus: CheckRudderPluginDatasourcesEnable
, pluginStatus: PluginStatus
, newUuid : () => ModificationId
, updateAll : UpdateCause => Unit
) extends Loggable {
......
scheduledTask = Some(source.subscribe())
} else {
// the plugin is disabled, does nothing
DataSourceLogger.warn(s"The datasource with id '${datasource.id.value}' is enabled but the plugin is disabled (reason: ${pluginStatus.enabledStatus}). Not scheduling future runs for it.")
DataSourceLogger.warn(s"The datasource with id '${datasource.id.value}' is enabled but the plugin is disabled (reason: ${pluginStatus.current}). Not scheduling future runs for it.")
}
} else {
DataSourceLogger.trace(s"The datasource with id '${datasource.id.value}' is disabled. Not scheduling future runs for it.")
src/test/scala/com/normation/plugins/datasources/UpdateHttpDatasetTest.scala
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
import scala.util.Random
import bootstrap.rudder.plugin.CheckRudderPluginDatasourcesEnable
import bootstrap.rudder.plugin.DatasourcesStatus
import com.normation.plugins.AlwaysEnabledPluginStatus
......
val uuidGen = new StringUuidGeneratorImpl()
}
object Enabled extends CheckRudderPluginDatasourcesEnable {
val hasLicense = false
val isEnabled = true
val enabledStatus = DatasourcesStatus.Enabled
val licenseInformation = None
}
sequential
"Update on datasource" should {
......
val dss = new DataSourceScheduler(
datasource.copy(enabled = false)
, testScheduler
, Enabled
, AlwaysEnabledPluginStatus
, () => ModificationId(MyDatasource.uuidGen.newUuid)
, action
)
......
val dss = new DataSourceScheduler(
datasource.copy(runParam = datasource.runParam.copy(schedule = NoSchedule(1.second)))
, testScheduler
, Enabled
, AlwaysEnabledPluginStatus
, () => ModificationId(MyDatasource.uuidGen.newUuid)
, action
)
......
val dss = new DataSourceScheduler(
datasource
, testScheduler
, Enabled
, AlwaysEnabledPluginStatus
, () => ModificationId(MyDatasource.uuidGen.newUuid)
, action
)

Also available in: Unified diff