首先,我们定义的 Stage 都实现了 StageDefinitionBuilder 接口,例如:
1 2 3 4 5 6 7
| @Slf4j @Component @CompileStatic class BakeStage implements StageDefinitionBuilder { public static final String PIPELINE_CONFIG_TYPE = "bake" }
|
其次,看一个 bean StageResolver,它在初始化的时候,将所有的 StageDefinitionBuilder 作为集合传递进去。
1 2 3 4 5 6 7 8 9
| @Bean public StageResolver stageResolver( Collection<StageDefinitionBuilder> stageDefinitionBuilders, Optional<Collection<SimpleStage>> simpleStages, PluginManager pluginManager) { Collection<SimpleStage> stages = simpleStages.orElseGet(ArrayList::new); stages.addAll(pluginManager.getExtensions(SimpleStage.class)); return new StageResolver(stageDefinitionBuilders, stages); }
|
并将所有的 bean 存入一个 map 中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public StageResolver( Collection<StageDefinitionBuilder> stageDefinitionBuilders, Collection<SimpleStage> simpleStages) { for (StageDefinitionBuilder stageDefinitionBuilder : stageDefinitionBuilders) { stageDefinitionBuilderByAlias.put(stageDefinitionBuilder.getType(), stageDefinitionBuilder); for (String alias : stageDefinitionBuilder.aliases()) { if (stageDefinitionBuilderByAlias.containsKey(alias)) { throw new DuplicateStageAliasException("xxxooo"); }
stageDefinitionBuilderByAlias.put(alias, stageDefinitionBuilder); } }
simpleStages.forEach( s -> stageDefinitionBuilderByAlias.put(s.getName(), new SimpleStageDefinitionBuilder(s))); }
|
其中 map 的 key 与对应的 stage 的关系如下:
- type -> stage:
stageDefinitionBuilderByAlias.put(stageDefinitionBuilder.getType(), stageDefinitionBuilder)
- alias -> stage:
stageDefinitionBuilderByAlias.put(alias, stageDefinitionBuilder)
type 的默认实现是以 className 基础,将首字母小写后,去换掉结尾的 “Stage”、”StageDefinitionBuilder”,处理完成后做为 type。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| default @Nonnull String getType() { return getType(this.getClass()); }
static String getType(Class<? extends StageDefinitionBuilder> clazz) { String className = clazz.getSimpleName(); return className.substring(0, 1).toLowerCase() + className .substring(1) .replaceFirst("StageDefinitionBuilder$", "") .replaceFirst("Stage$", ""); }
|
alias 的获取方式是通过在 stage 的类上的注解:
1 2 3 4 5 6 7
| default Collection<String> aliases() { if (getClass().isAnnotationPresent(Aliases.class)) { return Arrays.asList(getClass().getAnnotation(Aliases.class).value()); }
return Collections.emptyList(); }
|
最后,在 StartStageHandler
的 stage.plan()
方法中,会调用 StageResolver
的 getStageDefinitionBuilder()
方法。
1 2 3 4 5
| @Override public @Nonnull StageDefinitionBuilder builderFor(@Nonnull Stage stage) { return stageResolver.getStageDefinitionBuilder( stage.getType(), (String) stage.getContext().get("alias")); }
|
在此方法中,会从 pipeline 相应的 stage 中,获取配置的 type 和 alias,并将它们作为 key,到 StageResolver
的 map stageDefinitionBuilderByAlias
中依次去 get。顺序是先用 type 去取,如果未取到,再用 alias 取到的 stage 对象。
1 2 3 4 5 6 7 8 9 10 11 12
| @Nonnull public StageDefinitionBuilder getStageDefinitionBuilder(@Nonnull String type, String typeAlias) { StageDefinitionBuilder stageDefinitionBuilder = stageDefinitionBuilderByAlias.getOrDefault( type, stageDefinitionBuilderByAlias.get(typeAlias));
if (stageDefinitionBuilder == null) { throw new NoSuchStageDefinitionBuilderException(type, stageDefinitionBuilderByAlias.keySet()); }
return stageDefinitionBuilder; }
|
因此,在编写一个 Stage 类时,需要做完如下两个件事:
- 在 xxxStage 类中实现
getType()
方法以覆盖默认获取 type 的方法
- 依靠 xxxStage 类的名称(需要与前端沟通好相应的 type 字段和 alias 字段)
前者比较明显易懂,后者不是特别容易理解,但是 spinnaker 里面大多数是以后者的形式出现的。