본문 바로가기
WIKI 기술 지식 베이스

Actions Registry

원문 보기 위키 갱신

Actions Registry (알파)

Actions Registry Service는 Backstage 백엔드 플러그인 내에서 실행할 수 있는 작업을 위한 분산 레지스트리를 제공하도록 설계된 핵심 서비스예요.

출처: 문서

본문

개요

Actions Registry Service는 Backstage 백엔드 플러그인 내에서 실행할 수 있는 작업을 위한 분산 레지스트리를 제공하도록 설계된 핵심 서비스예요. 이 서비스는 플러그인이 잘 정의된 스키마와 실행 로직을 가진 재사용 가능한 작업을 등록할 수 있게 하여, Backstage 생태계 전반의 일관성과 재사용성을 촉진해요.

작업 구조

서비스에 등록된 각 작업은 다음을 포함하는 ActionsRegistryActionOptions 타입을 따라야 해요:

필수 속성

  • name: 작업의 고유 식별자 (string)

  • title: 작업의 사람이 읽을 수 있는 제목 (string)

  • description: 작업이 하는 일에 대한 자세한 설명 (string)

  • schema: 스키마 정의를 포함하는 객체

  • input: 입력 검증을 위한 Zod 스키마를 반환하는 함수

  • output: 출력 검증을 위한 Zod 스키마를 반환하는 함수

  • secrets: (선택) 시크릿 검증을 위한 Zod 스키마를 반환하는 함수. 아래 시크릿 참조.

  • action: 작업 로직을 실행하는 비동기 함수

선택 속성

  • visibilityPermission: 권한 프레임워크를 통해 작업에 대한 가시성과 접근을 제어하는 BasicPermission. 아래 권한 참조.

  • attributes: 동작 플래그를 포함하는 객체:

  • destructive: 작업이 데이터를 수정하거나 삭제하는지 나타내는 boolean

  • idempotent: 작업을 여러 번 실행해도 같은 결과를 내는지 나타내는 boolean

  • readOnly: 작업이 수정 없이 데이터만 읽는지 나타내는 boolean

작업 컨텍스트

작업이 실행되면 다음을 포함하는 컨텍스트 객체(ActionsRegistryActionContext)를 받아요:

  • input: 정의된 입력 스키마와 일치하는 검증된 입력 데이터

  • secrets: 정의된 시크릿 스키마와 일치하는 검증된 시크릿 데이터. 시크릿 스키마가 선언되지 않았으면 undefined.

  • logger: 작업 내 로깅을 위한 LoggerService 인스턴스

  • credentials: 인증 및 권한 부여를 위한 BackstageCredentials

서비스 사용

작업 등록

Actions Registry Service에 작업을 등록하는 예시는 다음과 같아요:

import { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';export function registerMyActions(actionsRegistry: ActionsRegistryService) {  // Register a simple read-only action  actionsRegistry.register({    name: 'fetch-user-info',    title: 'Fetch User Information',    description: 'Retrieves user information from the catalog',    schema: {      input: z =>        z.object({          userRef: z.string(),          includeGroups: z.boolean().optional(),        }),      output: z =>        z.object({          user: z.object({            name: z.string(),            email: z.string(),            groups: z.array(z.string()).optional(),          }),        }),    },    attributes: {      readOnly: true,      idempotent: true,    },    action: async ({ input, logger, credentials }) => {      logger.info(`Fetching user info for ${input.userRef}`);      // Perform the action logic here      const user = await fetchUserFromCatalog(input.userRef, credentials);      return {        output: {          user: {            name: user.name,            email: user.email,            groups: input.includeGroups ? user.groups : undefined,          },        },      };    },  });  // Register a destructive action  actionsRegistry.register({    name: 'delete-entity',    title: 'Delete Entity',    description: 'Removes an entity from the catalog',    schema: {      input: z =>        z.object({          entityRef: z.string(),          force: z.boolean().optional(),        }),      output: z =>        z.object({          deletedEntities: z.array(z.string()),        }),    },    attributes: {      destructive: true,      idempotent: false,    },    action: async ({ input, logger, credentials }) => {      logger.warn(`Deleting entity ${input.entityRef}`);      // Perform the deletion logic here      const { deletedEntities } = await deleteEntityFromCatalog(        input.entityRef,        input.force,        credentials,      );      return {        output: deletedEntities,      };    },  });}

플러그인에서 서비스 접근

플러그인에서 Actions Registry Service를 사용하려면 의존성 주입을 통해 접근하세요:

import {  createBackendPlugin,  coreServices,} from '@backstage/backend-plugin-api';import { actionsRegistryServiceRef } from '@backstage/backend-plugin-api/alpha';export const myPlugin = createBackendPlugin({  pluginId: 'my-plugin',  register(env) {    env.registerInit({      deps: {        actionsRegistry: actionsRegistryServiceRef,        logger: coreServices.logger,      },      async init({ actionsRegistry, logger }) {        logger.info('Registering actions...');        registerMyActions(actionsRegistry);        logger.info('Actions registered successfully');      },    });  },});

권한

작업은 선택적으로 visibilityPermission을 선언해 Backstage 권한 프레임워크를 통해 가시성과 접근을 제어할 수 있어요. visibilityPermission은 BasicPermission이어야 해요(리소스 권한이 아님). 설정되면 작업은 목록에서만 보이고 권한이 부여된 호출자만 접근할 수 있어요.

Actions Service 또는 /.backstage/actions/v1/... HTTP 엔드포인트를 통해 접근할 때 권한 정책이 거부한 작업은 목록 결과에서 필터링되고 호출 시 존재하지 않는 것처럼 404 Not Found를 반환해요.

작업에 선언된 권한은 PermissionsRegistryService에 자동으로 등록되어 권한 정책 시스템에 나타나요.

작업에 권한 추가

import { createPermission } from '@backstage/plugin-permission-common';// Define a permission for your actionconst myDeletePermission = createPermission({  name: 'my-plugin.actions.deleteEntity',  attributes: { action: 'delete' },});actionsRegistry.register({  name: 'delete-entity',  title: 'Delete Entity',  description: 'Removes an entity from the catalog',  visibilityPermission: myDeletePermission,  schema: {    input: z => z.object({ entityRef: z.string() }),    output: z => z.object({ deleted: z.boolean() }),  },  action: async ({ input }) => {    // action logic    return { output: { deleted: true } };  },});

visibilityPermission 필드가 없는 작업은 모든 호출자에게 계속 보이고 접근 가능하며, 이전 버전과의 호환성을 유지해요.

시크릿

작업은 secrets 스키마를 선언해 API 토큰, 개인 액세스 토큰, 또는 Backstage 자체 인증 시스템의 일부가 아닌 기타 민감한 값 같은 외부 자격 증명을 최종 사용자에게 요청할 수 있어요. 시크릿은 입력 스키마와 분리되어 유지되므로 작업이 MCP 도구로 노출될 때 도구 정의나 LLM 컨텍스트에 절대 나타나지 않아요.

시크릿 스키마 선언

input과 output과 함께 schema 객체에 secrets 함수를 추가하세요. 입력 스키마와 같은 방식으로 작동하며 Zod 인스턴스를 받아 Zod 객체 스키마를 반환해요:

actionsRegistry.register({  name: 'create-issue',  title: 'Create GitHub Issue',  description: 'Creates an issue in a GitHub repository',  schema: {    input: z =>      z.object({        repo: z.string(),        title: z.string(),        body: z.string().optional(),      }),    output: z =>      z.object({        issueUrl: z.string(),      }),    secrets: z =>      z.object({        githubToken: z          .string()          .describe('GitHub Personal Access Token with repo scope'),      }),  },  attributes: {    destructive: false,  },  action: async ({ input, secrets, credentials }) => {    const octokit = new Octokit({ auth: secrets.githubToken });    const { data } = await octokit.issues.create({      owner: input.repo.split('/')[0],      repo: input.repo.split('/')[1],      title: input.title,      body: input.body,    });    return { output: { issueUrl: data.html_url } };  },});

작업 컨텍스트의 secrets 필드는 선언된 스키마를 기반으로 완전히 타입화돼요. secrets 스키마가 없는 작업은 secrets 필드로 undefined를 받아요.

시스템에서 시크릿이 흐르는 방식

시크릿은 입력이 검증되는 것과 같은 방식으로 Zod 스키마에 대해 검증돼요. 시크릿이 필수인데 제공되지 않거나 검증에 실패하면 작업이 InputError를 반환해요. 시크릿 스키마를 선언하지 않은 작업에 시크릿을 제공하면 요청도 거부돼요.

시크릿 스키마는 목록 엔드포인트가 반환하는 작업 메타데이터에 포함되므로, 호출자는 작업을 호출하기 전에 어떤 시크릿이 필요한지 발견할 수 있어요.

모범 사례

명명 규칙

  • kebab-case 사용: 작업 이름은 kebab-case여야 합니다 (예: fetch-user-info, create-repository)

  • 설명적으로: 작업이 하는 일을 명확히 설명하는 이름을 선택하세요.

  • 중복 피하기: 플러그인 컨텍스트는 별도이므로 작업 이름에 플러그인 이름을 포함하지 마세요.

  • 동사 사용: 작업 이름을 작업을 설명하는 동사로 시작하세요 (예: fetch, create, delete, update)

오류 처리

작업이 문제를 만나면 @backstage/errors의 오류 클래스를 사용하세요. 이러한 오류는 Actions Service와 MCP Actions Backend 같은 소비자가 인식하며, 호출자에게 오류 메시지를 표시해요. 인식되지 않은 오류 타입은 일반적인 500 Server Error를 초래할 수 있어요.

import { NotFoundError, NotAllowedError } from '@backstage/errors';actionsRegistry.register({  name: 'update-resource',  title: 'Update Resource',  description: 'Updates a resource by ID',  schema: {    input: z => z.object({ id: z.string() }),    output: z => z.object({ updated: z.boolean() }),  },  attributes: { destructive: false, readOnly: false, idempotent: true },  action: async ({ input, credentials }) => {    const resource = await getResource(input.id);    if (!resource) {      throw new NotFoundError(`Resource ${input.id} not found`);    }    if (!hasPermission(credentials, resource)) {      throw new NotAllowedError(        `Insufficient permissions for resource ${input.id}`,      );    }    await updateResource(resource);    return { output: { updated: true } };  },});

작업 속성 참조

| 속성 | 타입 | 기본값 | 설명 | | destructive | boolean | 읽기 전용이면 false, 그렇지 않으면 true | 작업이 데이터를 수정하거나 삭제함을 나타냅니다. 주의해서 사용하세요. | | idempotent | boolean | false | 작업을 여러 번 실행해도 같은 결과를 낼 수 있음을 나타냅니다. | | readOnly | boolean | false | 작업이 수정 없이 데이터만 읽음을 나타냅니다. |

이 속성은 작업 소비자가 작업의 특성에 따라 적절한 안전장치, 재시도, 또는 최적화를 이해하고 구현하는 데 도움이 돼요.

더 알아보기 (Learn more)