其他问题

本文为您介绍使用云效产品功能遇到的问题与解决方案汇总。

成员使用服务管理实现RAM用户资源共享

  • 当您的组织成员有多个RAM用户时,且每个RAM用户归属的阿里云账号都有自己的云服务资源,云效支持将多个RAM用户的阿里云资源添加至组织,实现云效流水线构建,部署发布任务。

  • 比如,您的组织有 A和 B成员,您可通过添加RAM用户的方式,将 A 和 B成员的ECS云服务资源都加入到您的组织实现资源共享。

    • 通过云效工作台,组织管理后台 > 成员管理 > 成员 ,单击添加成员把RAM用户 A 和 B 都加入到您的组织。

      高的 (78).png

    • 成员 A 和 B 分别登录云效,并在服务连接管理中分别添加 A 和 B 的 ECS 服务连接,使用范围具体结合自己的需求选择。

      高的 (87).png

      重要

      如果成员是RAM用户(子账号),可能会出现授权失败的情况,请联系阿里云账号为RAM用户授权AliyunRAMFullAccess权限。

    • 服务连接创建完成后,主机组管理 > 新建主机组,可切换不同账号下的RAM授权来使用服务连接。

      高的 (90).png

授权管理—添加多个阿里云账号的授权服务

当您需要将其他阿里云账号添加到组织中时,可在添加成员中通过链接邀请,复制邀请链接发送给相应成员,被邀请者只需打开链接并接受邀请,即可加入到对应云效组织。高的 (2)

通过钉钉进行人工卡点审批

目前流水线Flow支持添加工具 > 人工卡点任务插件,您可以在Flow流水线中,添加人工卡点任务,实现钉钉接收发布前的审批确认,直接在钉钉客户端上操作流水线Flow审批任务。

步骤一:绑定钉钉组织和个人信息

  • 组织绑定:组织。

  • 个人绑定:组织。

步骤二:流水线添加人工卡点并执行流水线

在流水线编排中,添加人工卡点组件。

123123

选择具体的验证方式、验证者类型和验证人,保存触发流水线运行。

高的 (77).png高的 (84).png

收到钉钉审批消息,进行验证操作。

image

解析红线异常问题

解析红线异常的问题排查与解决方案。

  • 复制错误信息

    单击错误信息,弹框出来的内容复制出来 .

    image.png

    双击复制红线信息。

    image.png

  • 运行排查用例

    将红线信息数据放入下面用例里进行调试,排查问题.

    image.png排查所用到的示例代码如下:

    import com.alibaba.fastjson.JSON;
    
    import java.io.Serializable;
    import java.lang.reflect.Field;
    import java.util.*;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public class StatInfoTest {
    
        private static Pattern STAT_INFO_TITLE_PATTERN = Pattern.compile("^STAT_INFO_TITLE:\\s*(.+)\\s*");
        private static Pattern REDLINE_INFO_PATTERN = Pattern.compile("^REDLINE_ITEM_LINE:\\s*(.+)\\s*");
        private static Pattern STAT_PATTERN = Pattern.compile("^STAT_(VALUE|NAME|TITLE|URL|STYLE)_([^:]+):\\s*(.*)\\s*");
    
    
        public static void main(String[] args) {
            // 下面为一个失败的示例数据
            //String statOutMapStr = "{\"10_1682051393916__10_1682051404781\":[\"STAT_INFO_TITLE: \\\"\\\"\",\"STAT_NAME_Total: 缺陷\",\"STAT_VALUE_Total: ww\",\"STAT_STYLE_Total: Error\",\"STAT_NAME_Blocker: 漏洞\",\"STAT_VALUE_Blocker: ww\",\"STAT_STYLE_Blocker: Warning\",\"REDLINE_ITEM_LINE: {\\\"key\\\":\\\"Blocker\\\",\\\"threshold\\\":43,\\\"checkVal\\\":ww,\\\"type\\\":\\\"LE\\\",\\\"checked\\\":true,\\\"checkResult\\\":false}\",\"STAT_NAME_Critical: 坏味道\",\"STAT_VALUE_Critical: ww\",\"STAT_STYLE_Critical: Warning\",\"REDLINE_ITEM_LINE: {\\\"key\\\":\\\"Critical\\\",\\\"threshold\\\":34,\\\"checkVal\\\":ww,\\\"type\\\":\\\"LE\\\",\\\"checked\\\":true,\\\"checkResult\\\":false}\",\"STAT_NAME_Major: 覆盖率\",\"STAT_VALUE_Major: ww0\",\"STAT_STYLE_Major: Default\",\"REDLINE_ITEM_LINE: {\\\"key\\\":\\\"Major\\\",\\\"threshold\\\":4,\\\"checkVal\\\":ww0,\\\"type\\\":\\\"LE\\\",\\\"checked\\\":true,\\\"checkResult\\\":false}\",\"STAT_URL__REPORT: 127.0.0.1/dashboard?id=13212323%3A31313213213\"]}";
            String  statOutMapStr="请将卡片上的数据信息复制到此处进行调试";
            parseStatInfo(statOutMapStr);
        }
    
        public static void parseStatInfo(String statOutMapStr) {
            Map<String, List<String>> statOutMap = JSON.parseObject(statOutMapStr, new HashMap<String, List<String>>().getClass());
            List<StatGroup> statGroups = new ArrayList<>();
            statOutMap.forEach((k, params) -> {
                StatGroup statGroup = new StatGroup<StatInfo>();
                Map<String, StatInfo> statInfoMap = new LinkedHashMap<>(64);
                List<RedlineResultItem> redlines = new ArrayList<>(64);
                Optional.ofNullable(params).orElse(new ArrayList<>())
                        .forEach(str -> {
                            Matcher matcher = STAT_PATTERN.matcher(str);
                            if (matcher.find()) {
                                String fieldName = matcher.group(1).toLowerCase();
                                String key = matcher.group(2);
                                String value = matcher.group(3);
                                StatInfo statInfo = statInfoMap.get(key);
                                if (statInfo == null) {
                                    statInfo = new StatInfo();
                                    statInfo.setKey(key);
                                }
                                statInfo.setField(fieldName, value);
                                statInfoMap.put(key, statInfo);
                            }
                            // check redlines
                            Matcher redlineMatcher = REDLINE_INFO_PATTERN.matcher(str);
                            if (redlineMatcher.find()) {
                                String redlineResult = redlineMatcher.group(1);
                                RedlineResultItem redlineItem = JSON.parseObject(redlineResult, RedlineResultItem.class);
                                // check if valid redline item
                                if (redlineItem.getKey() != "" && redlineItem.getThreshold() != null) {
                                    redlines.add(redlineItem);
                                }
                            }
                        });
                statGroup.setTitle(getStatInfoTitle(params));
                statGroup.setStatInfoMap(statInfoMap);
                statGroup.setRedlineResult(redlines);
                statGroups.add(statGroup);
            });
            System.out.println(JSON.toJSONString(statGroups));
        }
    
        private static String getStatInfoTitle(List<String> params) {
            String title = null;
            if (params != null) {
                for (String param : params) {
                    Matcher matcher = STAT_INFO_TITLE_PATTERN.matcher(param);
                    if (matcher.find()) {
                        title = matcher.group(1);
                        break;
                    }
                }
            }
            return title;
        }
    
        static class StatGroup<T> {
            private String title;
            private Map<String, T> statInfoMap;
            private List<RedlineResultItem> redlineResult;
    
            public String getTitle() {
                return title;
            }
    
            public void setTitle(String title) {
                this.title = title;
            }
    
            public Map<String, T> getStatInfoMap() {
                return statInfoMap;
            }
    
            public void setStatInfoMap(Map<String, T> statInfoMap) {
                this.statInfoMap = statInfoMap;
            }
    
            public List<RedlineResultItem> getRedlineResult() {
                return redlineResult;
            }
    
            public void setRedlineResult(
                    List<RedlineResultItem> redlineResult) {
                this.redlineResult = redlineResult;
            }
        }
    
        public static class RedlineResultItem {
    
            private String key;
            private Integer threshold;
            private Integer checkVal;
            private String type;
            private Boolean checked;
            private Boolean checkResult;
    
            public String getKey() {
                return key;
            }
      
            public void setKey(String key) {
                this.key = key;
            }
    
            public Integer getThreshold() {
                return threshold;
            }
    
            public void setThreshold(Integer threshold) {
                this.threshold = threshold;
                }
    
            public Integer getCheckVal() {
                return checkVal;
            }
    
            public void setCheckVal(Integer checkVal) {
                this.checkVal = checkVal;
            }
    
            public String getType() {
                return type;
            }
    
            public void setType(String type) {
                this.type = type;
            }
    
            public Boolean getChecked() {
                return checked;
            }
    
            public void setChecked(Boolean checked) {
                this.checked = checked;
            }
    
            public Boolean getCheckResult() {
                return checkResult;
            }
    
            public void setCheckResult(Boolean checkResult) {
                this.checkResult = checkResult;
            }
        }
    
        static class StatInfo implements Serializable {
            private static final long serialVersionUID = -7424772230982171781L;
    
            private String key;
            private String value;
              private String name;
            private String title;
            private String style;
            private String url;
    
    
            public String getKey() {
                return key;
            }
    
            public void setKey(String key) {
                this.key = key;
            }
    
            public String getValue() {
                return value;
            }
    
            public void setValue(String value) {
                this.value = value;
            }
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            public String getTitle() {
                return title;
            }
    
            public void setTitle(String title) {
                this.title = title;
            }
    
            public String getStyle() {
                return style;
            }
    
            public void setStyle(String style) {
                this.style = style;
            }
    
            public String getUrl() {
                return url;
            }
    
            public void setUrl(String url) {
                this.url = url;
            }
    
            public void setField(String fieldName, String value) {
                try {
                    Field field = getClass().getDeclaredField(fieldName);
                    field.set(this, value);
                } catch (Exception e) {
                }
            }
        }
    }

jenkins任务步骤出现403错误

jenkins任务步骤出现403错误的原因和解决方案。

  • 问题现象

    image

  • 问题原因

    调用Jenkins API 支持使用密码和Token两种方式。由于Jenkins在新版本进行了调整,不允许直接使用密码的方式调用API,会返回403报错,所以Jeknis 任务推荐使用API Token。

  • 解决方案

    • 创建API Token打开http://Jenkins_IP:8080/user/admin/configure,注意换成自己真实的Jenkins地址,如果没用admin用户,user后也要替换成存在的用户名,比如:http://localhost:8080/user/wangli/configure。按下图添加Token,记得要将Token复制出来备用,最后点保存按钮。image

    • 在流水线中使用API Token选择凭据类型时选择服务连接,然后在新建服务连接,新建服务授权/证书中,依次输入服务地址,用户名和刚生成的Token即可。

      image

调用阿里云产品 API 异常

API异常的常见问题。

排查方案

  • 主账号确认排查AliyunRDCDefaultRole角色是否被删除。

    image

    如果AliyunRDCDefaultRole 被删除了,就让主账号参考下图进行重新授权。

    image

  • 如果AliyunRDCDefaultRole 仍存在,请检查AliyunRDCDefaultRole对应的权限策略AliyunRDCRolePolicy是否正确。

    image

    • 如果 AliyunRDCDefaultRole 中有多个权限策略,请删除AliyunRDCRolePolicy以外的其他权限策略。

    • 如果AliyunRDCRolePolicy 不存在,就先删除AliyunRDCDefaultRole,然后再参考方案1重新授权。

    • 如果AliyunRDCRolePolicy 存在,但调用仍报错,就先解除AliyunRDCRolePolicy 授权,然后删除AliyunRDCDefaultRole,最后再参考方案1重新授权。

GetPipelineRun Action说明

Action

对应API

功能

示例

说明

PassPipelineValidate

通过人工卡点

{
    "disable": false,
    "type": "PassPipelineValidate",
    "params":
    {
        "validators":
        [
            "12419650xxxxxx84126"
        ]
    }
}

disable: 是否有权限调用

validators:有权限通过人工卡点的用户的aliyunPk

RefusePipelineValidate

RefusePipelineValidator

拒绝人工卡点

{
    "disable": false,
    "type": "RefusePipelineValidator",
    "params":
    {
        "validators":
        [
            "12419650xxxxxx84126"
        ]
    }
}

disable: 是否有权限调用

validators: 有权限通过人工卡点的用户的aliyunPk

RetryPipelineJobRun

RetryPipelineJobRun

重试任务运行

{
    "disable": false,
    "type": "RetryPipelineJobRun"
}

disable: 是否有权限调用

StopPipelineJobRun

StopPipelineJobRun

取消任务运行

{
    "disable": false,
    "type": "StopPipelineJobRun"
}

disable: 是否有权限调用

SkipPipelineJobRun

SkipPipelineJobRun

跳过失败任务

{
    "disable": false,
    "type": "SkipPipelineJobRun"
}

disable: 是否有权限调用

LogPipelineJobRun

LogPipelineJobRun

查看任务执行日志

{
    "disable": false,
    "type": "LogPipelineJobRun"
}

disable: 是否有权限调用

GetVMDeployOrder

GetVMDeployOrder

获取ecs部署单详情

{
    "disable": false,
    "type": "GetVMDeployOrder",
    "params":
    {
        "deployOrderId": 1111111
    }
}

disable: 是否有权限调用

deployOrderId: 部署单id

GetPipelineEmasArtifactUrl

GetPipelineEmasArtifactUrl

获取EMAS构建产物临时下载地址

{
    "disable": false,
    "type": "GetPipelineEmasArtifactUrl",
    "params":
    {
        "serviceConnectionId": 11112221,
        "files":
        [
            {
                "fileName": "app-release-unsigned.apk",
                "emasJobInstanceId": "pxxx232",
                "md5": "ssaxxxasxsxsxs"
            }
        ]
    }
}

serviceConnectionId: 服务连接id

files: 构建产物,支持多个构建产物

emasJobInstanceId: emas 任务id

md5: 构建产物md5

disable: 是否有权限调用

GetPipelineArtifactUrl

GetPipelineArtifactUrl

获取流水线构建产物临时下载地址

{
    "disable": false,
    "type": "GetPipelineArtifactUrl",
    "params":
    {
        "files":
        [
            {
                "fileName": "Artifacts_1520621.tgz",
                "filePath": "aone2/212xx33/2332xxsassa/Artifacts_1520621.tgz"
            }
        ]
    }
}

disable: 是否有权限调用

files: 构建产物,支持多个构建产物

fileName: 构建产物名称

filePath: 构建产物路径

GetPipelineScanReportUrl

GetPipelineScanReportUrl

获取扫描报告临时下载地址

{"disable":false,"type":"GetPipelineScanReportUrl","params":{"reportPath":"assets/1xxx/1xxx/05e0edb9xxxxxxxx/index.html","reportInfo":{"Major":{"name":"issue","style":"text-danger","title":"Major","value":"6","key":"Major",}"Total":{"name":"issue","style":"text-danger","title":"Total","value":"6","key":"Total"}"Critical":{"name":"issue","style":"text-danger","title":"Critical","value":"0","key":"Critical"}"Blocker":{"name":"issue","style":"text-danger","title":"Blocker","value":"0","key":"Blocker"}"_REPORT":{"title":"report","value":"link","key":"_REPORT","url":"https://flow.aliyun.com/assets/2xxxxx/1xxxx/21212xxxxxxxxxxx/index.html"}"File":{"name":"issue","style":"text-danger","title":"File","value":"4","key":"File"}}}}

disable: 有没有权限调用

reportPath: 报告文件路径

reportInfo: 报告信息

调用API提示无权限

  • 问题:

    调用API接口提示401无权限: com.aliyun.tea.TeaException: code: 401, InvalidParam.NoPermission request 。

  • 解决方案:

    出现报错是由于当前账号没有权限调用云效API,如果通过ak,sk 鉴权方式调用,且调用账号为RAM子账号,需要子账号拥有AliyunRDCFullAccess 权限。