Config Service

The Config Service provides a centralized persistent store for any configuration file used in the TMT Software System. All versions of configuration files are retained, providing a historical record of each configuration file. This client exposes simple methods to access and manage configuration files.

Rules and Checks

  1. The config file path must not contain !#<>$%&'@^``~+,;= or any whitespace character
  2. If the input file is > 10MB or has lots of non ASCII characters, then for optimization, server will archive it in annex store.
  3. Large and binary files can be forced to go to the annex store by using annex=true flag in create operation.

Model Classes

  • ConfigData: Represents the contents of the files being managed. It wraps blob object.
  • ConfigFileInfo: Represents information about a config file stored in the Config Service.
  • ConfigFileRevision: Represents information about a specific version of a config file.
  • ConfigId: Represents an identifier associated with a revision of a configuration file, often generated by create or update methods.
  • ConfigMetadata: Represents metadata information about the Config Server.
  • FileType: Represents the type of storage for a configuration file. Currently, two types are supported Normal(small, text files) and Annex(Large, Binary files).

Example for creation ConfigData and accessing data with helper function

Typescript
source
const dataArray = ['file-contents'] const configDataFromBlob: ConfigData = ConfigData.from(new Blob(dataArray)) const configDataFromFile: ConfigData = ConfigData.fromFile(new File(dataArray, 'filename')) const configDataFromString: ConfigData = ConfigData.fromString('someFileDataAsString') //accessing data const dataAsBlobContent: Blob = configDataFromString.toBlob() // notice use of async-await here. because this is an IO read call const readData = async (configData: ConfigData) => { const dataAsString: string = await configData.fileContentAsString() }

Note: Models other than ConfigData are simple TypeScript classes and do not have special helper methods unlike ConfigData.

Pre-requisite

In order to use config service

  1. The Location Service and Config Service Server needs to be running in the network
  2. The necessary configuration, environment variables or system properties should be defined to point to the correct host and port number(s) for the Location Service nodes.
  3. Authorization Token with correct access role. Documentation on how to fetch authorization token could be found here.

Creation of Config Service

To create a client for Config service

Config Service constructor takes TokenFactory as an input argument.

Typescript
sourceconst tokenFactory = () => auth.token

const configService: ConfigService = await ConfigService(tokenFactory)
Async-Await

Note that the examples are using async/await which makes handling of promises more readable.

Usages of Config Service

Type Definitions of all methods can be found here

Creating File

This method takes path at which configData needs to be saved in the Config Service along with meta information i.e whether to be saved as annex or normal file and comment. After saving, it returns ConfigId which can be used to access the saved file in the future using query methods.

Type definitions of create method can be found here

Typescript
sourceconst path = 'esw/sequencer/obsMode.conf'
const comment = 'observation configuration on 21st november 2020'
const author = 'OCS-Sequencer admin: Dave'
const data = `
esw-sm {
  obsModes: {
    IRIS_Darknight: {
      resources: [IRIS, TCS, NFIRAOS]
      sequencers: [IRIS, ESW, TCS]
    },
    IRIS_Cal: {
      resources: [IRIS, NSCU, NFIRAOS]
      sequencers: [IRIS, ESW, AOESW]
    },
    WFOS_Cal: {
      resources: [WFOS]
      sequencers: [WFOS, ESW]
    }
  }
}
`
const configData = ConfigData.fromString(data)

const sequencerConfigId: ConfigId = await configService.create(path, configData, false, comment)

Updating File

This method takes path at which configData needs to be updated in the Config Service along with comment. After updating, it returns ConfigId which can be used to access the updated file in future using query methods.

Type definitions of update method can be found here

Typescript
sourceconst filePath = 'esw/sequencer/obsMode.conf'
const commentOnUpdate = 'observation configuration on 23rd november 2020'
const updatedData = `
esw-sm {
  obsModes: {
    IRIS_Darknight: {
      resources: [IRIS]
      sequencers: [IRIS]
    },
    IRIS_Cal: {
      resources: [IRIS, NSCU, NFIRAOS]
      sequencers: [IRIS, ESW, AOESW]
    }
  }
}
`
const updatedConfigData = ConfigData.fromString(updatedData)

const newSequencerConfigId: ConfigId = await configService.update(filePath, updatedConfigData, commentOnUpdate)

Fetching Files

The Config Service provides multiple ways to fetch a file from the svn repository based on Time, ConfigId,Active, and Latest Revision.

Following are the methods available to fetch files:

  • getActive
  • getById
  • getByTime
  • getLatest

Examples for each of the methods are as follows:

  • getActive : This method return the ConfigData of the active version for that file if it exists otherwise returns undefined.

Type definitions of getActive method can be found here

Typescript
sourceconst file = 'esw/sequencer/obsMode.conf'

const maybeActiveConfigData: Option<ConfigData> = await configService.getActive(file)
  • getLatest : This method returns the ConfigData of the latest revision for that file if it exists otherwise returns undefined.

Type definitions of getLatest method can be found here

Typescript
sourceconst filepath = 'esw/sequencer/obsMode.conf'
const maybeLatestConfigData: Option<ConfigData> = await configService.getLatest(filepath)
  • getById : This method returns ConfigData based on the given ConfigId if it exists otherwise returns undefined.

Type definitions of getById method can be found here

Typescript
sourceconst configId: ConfigId = await configService.update(filePath, updatedConfigData, commentOnUpdate)

const maybeConfigData: Option<ConfigData> = await configService.getById(filepath, configId)
  • getByTime : This method gets the ConfigData at the given path as it existed at a given time-instance.

Note: -If time-instance is before the file was created, the initial version is returned. -If time-instance is after the last change, the most recent version is returned.

Type definitions of getByTime method can be found here

Typescript
sourceconst configId1: ConfigId = await configService.create(filePath, updatedConfigData, false, commentOnUpdate)

const beforeUpdate = new Date()

const configId2: ConfigId = await configService.update(filePath, configData, commentOnUpdate)
const afterUpdate = new Date()

const maybeConfigData: Option<ConfigData> = await configService.getByTime(filepath, beforeUpdate)
// maybeConfigData == updatedConfigData (i.e Initial revision of config)

const newlyUpdatedData: Option<ConfigData> = await configService.getByTime(filepath, afterUpdate)
// newlyUpdatedData == configData (i.e Latest revision of config)

Checking file existence

This method checks whether file exists at the given path and optional specific configId in the repository and returns true if it does exist or else false.

Type definitions of exists method can be found here

Typescript
sourceconst exists: boolean = await configService.exists(filePath)

const exist: boolean = await configService.exists(filePath, configId)

Deleting File

This method deletes a file located at specified path in the repository.

Type definitions of delete method can be found here

Typescript
sourceconst filePath = 'esw/sequencer/obsMode.conf'

await configService.delete(filePath, 'deleting the invalid config entry')

Listing files

This method list all the files for a given FileType (Annex or Normal) and an optional pattern string, it will list all files with the file path matching the given pattern.

Some pattern examples are: “/path/hcd/*.*”, “a/b/c/d.*”, “.*.conf”, “.*hcd.*”

Type definitions of list method can be found here

Typescript
sourceconst allAnnexFilesInfo: ConfigFileInfo[] = await configService.list({
  type: 'Annex'
})
const allNormalFilesInfo: ConfigFileInfo[] = await configService.list({
  type: 'Normal'
})
const allHcdAnnexFilesInfo: ConfigFileInfo[] = await configService.list({
  type: 'Annex',
  pattern: '.*hcd.*'
})
const allConfNormalFilesInfo: ConfigFileInfo[] = await configService.list({
  type: 'Normal',
  pattern: '.*conf'
})

Fetching revision history of a file

This method returns the history of revisions of the file at the given path for a range of period specified by from and to.

The size of the list can be restricted using maxResults.

Type definitions of history method can be found here

Typescript
sourceconst filePath = 'esw/sequencer/obsMode.conf'
const from = new Date(2019, 12, 31)
const to = new Date(2020, 12, 31)

// upto 200 file revisions from 31st dec 2019 - 31st dec 2020 will be fetched
const fileRevisions: ConfigFileRevision[] = await configService.history(filePath, from, to, 200)

Getting Config Service Metadata

This method returns metadata information about the Config Service. It includes:

  • repository directory
  • annex directory
  • min annex file size
  • max config file size

Type definitions of getMetadata method can be found here

Typescript
sourceconst metadata: ConfigMetadata = await configService.getMetadata()

Managing active versions

In its lifetime, a config file undergoes many revisions. An active version is a specific revision from a file’s history, and it is set by administrators.

historyActive

This method returns the history of active revisions of the file at the given path for a range of period specified by from and to. The size of the list can be restricted using maxResults.

Type definitions of historyActive method can be found here

Typescript
sourceconst filePath = 'esw/sequencer/obsMode.conf'
const from = new Date(2019, 12, 31)
const to = new Date(2020, 12, 31)

// upto 200 active file revisions from 31st dec 2019 - 31st dec 2020 will be fetched
const activeFileRevisions: ConfigFileRevision[] = await configService.historyActive(filePath, from, to, 200)

setActiveVersion

This method sets the “active version” to be the version provided for the file at the given path. If this method is never called in a Config’s lifetime, the active version will always be the version returned by create function.

Type definitions of setActiveVersion method can be found here

Typescript
sourceconst filePath = 'esw/sequencer/obsMode.conf'
const id: ConfigId = await configService.create(path, configData, false, comment)
const commentWhileSetting = `Making ${id} active on 1st dec 2020`

await configService.setActiveVersion(filePath, id, commentWhileSetting)

resetActiveVersion

This method resets the “active version” of the file at the given path to the latest version.

Type definitions of resetActiveVersion method can be found here

Typescript
sourceconst filePath = 'esw/sequencer/obsMode.conf'
const id: ConfigId = await configService.create(path, configData, false, comment)
const commentWhileResetting = `Making ${id} active version to latest as of 1st dec 2020`

await configService.resetActiveVersion(filePath, commentWhileResetting)

getActiveVersion

This method returns the revision ID which represents the “active version” of the file at the given path.

Type definitions of getActiveVersion method can be found here

Typescript
sourceconst filePath = 'esw/sequencer/obsMode.conf'

const maybeConfigId: Option<ConfigId> = await configService.getActiveVersion(filePath)

getActiveByTime

This method returns the content of active version of the file existed at given instant of Time

Type definitions of getActiveByTime method can be found here

Typescript
sourceconst filePath = 'esw/sequencer/obsMode.conf'
const at = new Date(2019, 12, 31)

const maybeConfigData: Option<ConfigData> = await configService.getActiveByTime(filePath, at)