Skip to content

Commit

Permalink
[8.15] [Automatic Import] Safely output the package manifest (#192316) (
Browse files Browse the repository at this point in the history
#192524)

# Backport

This will backport the following commits from `main` to `8.15`:
- [[Automatic Import] Safely output the package manifest
(#192316)](#192316)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Ilya
Nikokoshev","email":"ilya.nikokoshev@elastic.co"},"sourceCommit":{"committedDate":"2024-09-09T13:52:07Z","message":"[Automatic
Import] Safely output the package manifest (#192316)\n\n## Release
note\r\n\r\nFixes issues with rendering the package manifest in
Automatic Import.\r\n\r\n## Summary\r\n\r\nPreviously the multiline
output or special symbols in the user-provided\r\nstrings, like
description, were breaking YAML structure of the package\r\nmanifest.
The user would be confronted with a message like this, during\r\nthe
last step, after all the work of generating the integration
was\r\ncompleted.\r\n\r\nThe incorrect behavior can be observed in
detail with a failing test in\r\nthe first commit of the PR.\r\n\r\nIn
this PR, we change the manifest construction logic from
template\r\nrendering into TypeScript code. As a result, all
user-provided strings\r\nare correctly serialized. We keep as close as
possible to the original\r\nmanifest structure, also keeping the
parameter names.\r\n\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic
Machine
<elasticmachine@users.noreply.github.com>","sha":"521b6eec30cb5f8e4a81307110760527d21fd154","branchLabelMapping":{"^v8.16.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","release_note:fix","backport:prev-minor","v8.16.0","Team:Security-Scalability"],"title":"[Automatic
Import] Safely output the package
manifest","number":192316,"url":"https://github.com/elastic/kibana/pull/192316","mergeCommit":{"message":"[Automatic
Import] Safely output the package manifest (#192316)\n\n## Release
note\r\n\r\nFixes issues with rendering the package manifest in
Automatic Import.\r\n\r\n## Summary\r\n\r\nPreviously the multiline
output or special symbols in the user-provided\r\nstrings, like
description, were breaking YAML structure of the package\r\nmanifest.
The user would be confronted with a message like this, during\r\nthe
last step, after all the work of generating the integration
was\r\ncompleted.\r\n\r\nThe incorrect behavior can be observed in
detail with a failing test in\r\nthe first commit of the PR.\r\n\r\nIn
this PR, we change the manifest construction logic from
template\r\nrendering into TypeScript code. As a result, all
user-provided strings\r\nare correctly serialized. We keep as close as
possible to the original\r\nmanifest structure, also keeping the
parameter names.\r\n\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic
Machine
<elasticmachine@users.noreply.github.com>","sha":"521b6eec30cb5f8e4a81307110760527d21fd154"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/192316","number":192316,"mergeCommit":{"message":"[Automatic
Import] Safely output the package manifest (#192316)\n\n## Release
note\r\n\r\nFixes issues with rendering the package manifest in
Automatic Import.\r\n\r\n## Summary\r\n\r\nPreviously the multiline
output or special symbols in the user-provided\r\nstrings, like
description, were breaking YAML structure of the package\r\nmanifest.
The user would be confronted with a message like this, during\r\nthe
last step, after all the work of generating the integration
was\r\ncompleted.\r\n\r\nThe incorrect behavior can be observed in
detail with a failing test in\r\nthe first commit of the PR.\r\n\r\nIn
this PR, we change the manifest construction logic from
template\r\nrendering into TypeScript code. As a result, all
user-provided strings\r\nare correctly serialized. We keep as close as
possible to the original\r\nmanifest structure, also keeping the
parameter names.\r\n\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic
Machine
<elasticmachine@users.noreply.github.com>","sha":"521b6eec30cb5f8e4a81307110760527d21fd154"}}]}]
BACKPORT-->

Co-authored-by: Ilya Nikokoshev <ilya.nikokoshev@elastic.co>
  • Loading branch information
kibanamachine and ilyannn authored Sep 10, 2024
1 parent 983ae83 commit 8457287
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 47 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { Integration } from '../../common';
import { renderPackageManifestYAML } from './build_integration';
import yaml from 'js-yaml';

describe('renderPackageManifestYAML', () => {
test('generates the package manifest correctly', () => {
const integration: Integration = {
title: 'Sample Integration',
name: 'sample-integration',
description:
' This is a sample integration\n\nWith multiple lines and weird spacing. \n\n And more lines ',
logo: 'some-logo.png',
dataStreams: [
{
name: 'data-stream-1',
title: 'Data Stream 1',
description: 'This is data stream 1',
inputTypes: ['filestream'],
rawSamples: ['{field: "value"}'],
pipeline: {
processors: [],
},
docs: [],
samplesFormat: { name: 'ndjson', multiline: false },
},
{
name: 'data-stream-2',
title: 'Data Stream 2',
description:
'This is data stream 2\nWith multiple lines of description\nBut otherwise, nothing special',
inputTypes: ['aws-cloudwatch'],
pipeline: {
processors: [],
},
rawSamples: ['field="value"'],
docs: [],
samplesFormat: { name: 'structured' },
},
],
};

const manifestContent = renderPackageManifestYAML(integration);

// The manifest content must be parseable as YAML.
const manifest = yaml.safeLoad(manifestContent);

expect(manifest).toBeDefined();
expect(manifest.title).toBe(integration.title);
expect(manifest.name).toBe(integration.name);
expect(manifest.type).toBe('integration');
expect(manifest.description).toBe(integration.description);
expect(manifest.icons).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import AdmZip from 'adm-zip';
import nunjucks from 'nunjucks';
import { getDataPath } from '@kbn/utils';
import { join as joinPath } from 'path';
import { safeDump } from 'js-yaml';
import type { DataStream, Integration } from '../../common';
import { createSync, ensureDirSync, generateUniqueId, removeDirSync } from '../util';
import { createAgentInput } from './agent';
Expand All @@ -18,14 +19,18 @@ import { createPipeline } from './pipeline';

const initialVersion = '1.0.0';

export async function buildPackage(integration: Integration): Promise<Buffer> {
function configureNunjucks() {
const templateDir = joinPath(__dirname, '../templates');
const agentTemplates = joinPath(templateDir, 'agent');
const manifestTemplates = joinPath(templateDir, 'manifest');
const systemTestTemplates = joinPath(templateDir, 'system_tests');
nunjucks.configure([templateDir, agentTemplates, manifestTemplates, systemTestTemplates], {
autoescape: false,
});
}

export async function buildPackage(integration: Integration): Promise<Buffer> {
configureNunjucks();

const workingDir = joinPath(getDataPath(), `integration-assistant-${generateUniqueId()}`);
const packageDirectoryName = `${integration.name}-${initialVersion}`;
Expand Down Expand Up @@ -116,7 +121,82 @@ async function createZipArchive(workingDir: string, packageDirectoryName: string
return buffer;
}

function createPackageManifest(packageDir: string, integration: Integration): void {
/* eslint-disable @typescript-eslint/naming-convention */
/**
* Creates a package manifest dictionary.
*
* @param format_version - The format version of the package.
* @param package_title - The title of the package.
* @param package_name - The name of the package.
* @param package_version - The version of the package.
* @param package_description - The description of the package.
* @param package_logo - The package logo file name, if present.
* @param package_owner - The owner of the package.
* @param min_version - The minimum version of Kibana required for the package.
* @param inputs - An array of unique input objects containing type, title, and description.
* @returns The package manifest dictionary.
*/
function createPackageManifestDict(
format_version: string,
package_title: string,
package_name: string,
package_version: string,
package_description: string,
package_logo: string | undefined,
package_owner: string,
min_version: string,
inputs: Array<{ type: string; title: string; description: string }>
): { [key: string]: string | object } {
const data: { [key: string]: string | object } = {
format_version,
name: package_name,
title: package_title,
version: package_version,
description: package_description,
type: 'integration',
categories: ['security', 'iam'],
conditions: {
kibana: {
version: min_version,
},
},
policy_templates: [
{
name: package_name,
title: package_title,
description: package_description,
inputs: inputs.map((input) => ({
type: input.type,
title: `${input.title} : ${input.type}`,
description: input.description,
})),
},
],
owner: {
github: package_owner,
type: 'elastic',
},
};

if (package_logo !== undefined && package_logo !== '') {
data.icons = {
src: '/img/logo.svg',
title: `${package_title} Logo`,
size: '32x32',
type: 'image/svg+xml',
};
}
return data;
}
/* eslint-enable @typescript-eslint/naming-convention */

/**
* Render the package manifest for an integration.
*
* @param integration - The integration object.
* @returns The package manifest YAML rendered into a string.
*/
export function renderPackageManifestYAML(integration: Integration): string {
const uniqueInputs: { [key: string]: { type: string; title: string; description: string } } = {};

integration.dataStreams.forEach((dataStream: DataStream) => {
Expand All @@ -133,17 +213,22 @@ function createPackageManifest(packageDir: string, integration: Integration): vo

const uniqueInputsList = Object.values(uniqueInputs);

const packageManifest = nunjucks.render('package_manifest.yml.njk', {
format_version: '3.1.4',
package_title: integration.title,
package_name: integration.name,
package_version: initialVersion,
package_description: integration.description,
package_logo: integration.logo,
package_owner: '@elastic/custom-integrations',
min_version: '^8.13.0',
inputs: uniqueInputsList,
});
const packageData = createPackageManifestDict(
'3.1.4', // format_version
integration.title, // package_title
integration.name, // package_name
initialVersion, // package_version
integration.description, // package_description
integration.logo, // package_logo
'@elastic/custom-integrations', // package_owner
'^8.13.0', // min_version
uniqueInputsList // inputs
);

return safeDump(packageData);
}

function createPackageManifest(packageDir: string, integration: Integration): void {
const packageManifest = renderPackageManifestYAML(integration);
createSync(joinPath(packageDir, 'manifest.yml'), packageManifest);
}

This file was deleted.

0 comments on commit 8457287

Please sign in to comment.