2 | Spinnaker orca 如何找到对应的 stage
首先,我们定义的 Stage 都实现了 StageDefinitionBuilder 接口,例如:@Slf4j@Component@CompileStaticclass BakeStage implements StageDefinitionBuilder { public static fina
首先,我们定义的 Stage 都实现了 StageDefinitionBuilder 接口,例如:
@Slf4j
@Component
@CompileStatic
class BakeStage implements StageDefinitionBuilder {
public static final String PIPELINE_CONFIG_TYPE = "bake"
// ...
}
其次,看一个 bean StageResolver,它在初始化的时候,将所有的 StageDefinitionBuilder 作为集合传递进去。
@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 中
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。代码如下:
/** @return the stage type this builder handles. */
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 的类上的注解:
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() 方法。
@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 对象。
@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 里面大多数是以后者的形式出现的。