解决 K8sApi 部署后报 Unknown apiVersionKind apps/v1/Deployment is it registered?

该功能在本地调试时是正常的, 部署到服务器时报错。

Jdk11 + SpringBoot 2.7.5, 依赖:

        <dependency>
            <groupId>io.kubernetes</groupId>
            <artifactId>client-java</artifactId>
            <version>20.0.0</version>
        </dependency>

在执行: (V1Deployment)Yaml.load(yamlContent) 时报错: Unknown apiVersionKind apps/v1/Deployment is it registered?

经分析 /mnt/app/mvn-repo/io/kubernetes/client-java/20.0.0/client-java-20.0.0-sources.jar!/io/kubernetes/client/util/ModelMapper.java getClassNamesFromPackage 方法有问题,原代码如下:

private static List<String> getClassNamesFromPackage(ClassLoader classLoader, String pkg) throws IOException {
    ArrayList<String> names = new ArrayList<>();
    String packageName = pkg.replace(".", "/");
    URL packageURL = classLoader.getResource(packageName);

    if (packageURL.getProtocol().equals("jar")) {
      processJarPackage(packageURL, packageName, pkg, names);
    } else {
      processFilePackage(packageURL, pkg, names);
    }
    return names;
  }
  
  private static void processJarPackage(URL packageURL, String packageName, String pkg, ArrayList<String> names) throws IOException {
    String jarFileName = URLDecoder.decode(packageURL.getFile(), "UTF-8");
    JarFile jf = null;
    // jar: client in repository; nested: client in a fat jar
    if (jarFileName.startsWith("jar:") || jarFileName.startsWith("nested:")) {
      jf = ((JarURLConnection) packageURL.openConnection()).getJarFile();
    }
    // file: client is a file in target (unit test)
    if (jarFileName.startsWith("file:") ) {
      jarFileName = jarFileName.substring(5, jarFileName.indexOf("!"));
      jf = new JarFile(jarFileName);
    }
    if (jf == null) {
      logger.error("Loading classes from jar with error packageURL: {}", jarFileName);
      return;
    }
    logger.info("Loading classes from jar {}", jarFileName);
    Enumeration<JarEntry> jarEntries = jf.entries();
    while (jarEntries.hasMoreElements()) {
      processJarEntry(jarEntries.nextElement(), packageName, pkg, names);
    }
    jf.close();
  }
  
    private static void processJarEntry(JarEntry jarEntry, String packageName, String pkg, ArrayList<String> names) {
    String entryName = jarEntry.getName();
    if (entryName.startsWith(packageName) && entryName.length() > packageName.length() + 5) {
      entryName = entryName.substring(packageName.length() + 1, entryName.lastIndexOf('.'));
      names.add(pkg + "." + entryName);
    }
  }

image

原因是,在部署环境下, 扫描包时出错,扫描不到。修复如下:

    private static boolean inited = false;

    static void init() {
        if (inited) {
            return;
        }
        inited = true;
        initApiGroupMap();
        initApiVersionList();

        var names = findResources("classpath*:io/kubernetes/client/openapi/models/**/*.class");
        var modelMapperClass = ModelMapper.class;
        Field preBuiltField = null;
        try {
            preBuiltField = modelMapperClass.getDeclaredField("preBuiltClassesByGVK");
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }

        preBuiltField.setAccessible(true);

        Map<GroupVersionKind, Class<?>> preBuiltClassesByGVK = null;
        try {
            preBuiltClassesByGVK = (Map<GroupVersionKind, Class<?>>) preBuiltField.get(null);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        for (String classInfo : names) {
            Class<?> clazz = loadClass(classInfo, Yaml.class.getClassLoader());
            if (!KubernetesObject.class.isAssignableFrom(clazz)
                    && !KubernetesListObject.class.isAssignableFrom(clazz)) {
                continue;
            }

            Pair<String, String> groupAndOther = getApiGroup(clazz.getSimpleName());
            Pair<String, String> versionAndOther = getApiVersion(groupAndOther.getRight());

            String group = Strings.nullToEmpty(groupAndOther.getLeft());
            String version = versionAndOther.getLeft();
            String kind = versionAndOther.getRight();

            preBuiltClassesByGVK.put(new GroupVersionKind(group, version, kind), clazz);
        }
    }

    private static Pair<String, String> getApiGroup(String name) {
        return preBuiltApiGroups.entrySet().stream()
                .filter(e -> name.startsWith(e.getKey()))
                .map(e -> new ImmutablePair<>(e.getValue(), name.substring(e.getKey().length())))
                .findFirst()
                .orElse(new ImmutablePair<>(null, name));
    }

    private static Pair<String, String> getApiVersion(String name) {
        return preBuiltApiVersions.stream()
                .filter(v -> name.startsWith(v))
                .map(v -> new ImmutablePair<>(v.toLowerCase(), name.substring(v.length())))
                .findFirst()
                .orElse(new ImmutablePair<>(null, name));
    }

    private static Map<String, String> preBuiltApiGroups = new HashMap<>();

    private static void initApiGroupMap() {
        preBuiltApiGroups.put("Admissionregistration", "admissionregistration.k8s.io");
        preBuiltApiGroups.put("Apiextensions", "apiextensions.k8s.io");
        preBuiltApiGroups.put("Apiregistration", "apiregistration.k8s.io");
        preBuiltApiGroups.put("Apps", "apps");
        preBuiltApiGroups.put("Authentication", "authentication.k8s.io");
        preBuiltApiGroups.put("Authorization", "authorization.k8s.io");
        preBuiltApiGroups.put("Autoscaling", "autoscaling");
        preBuiltApiGroups.put("Batch", "batch");
        preBuiltApiGroups.put("Certificates", "certificates.k8s.io");
        preBuiltApiGroups.put("Core", "");
        preBuiltApiGroups.put("Extensions", "extensions");
        preBuiltApiGroups.put("Events", "events.k8s.io");
        preBuiltApiGroups.put("FlowControl", "flowcontrol.apiserver.k8s.io");
        preBuiltApiGroups.put("Networking", "networking.k8s.io");
        preBuiltApiGroups.put("Policy", "policy");
        preBuiltApiGroups.put("RbacAuthorization", "rbac.authorization.k8s.io");
        preBuiltApiGroups.put("Scheduling", "scheduling.k8s.io");
        preBuiltApiGroups.put("Settings", "settings.k8s.io");
        preBuiltApiGroups.put("Storage", "storage.k8s.io");
    }

    private static List<String> preBuiltApiVersions = new ArrayList<>();

    private static void initApiVersionList() {
        // Order important
        preBuiltApiVersions.add("V2beta1");
        preBuiltApiVersions.add("V2beta2");
        preBuiltApiVersions.add("V2alpha1");
        preBuiltApiVersions.add("V1beta2");
        preBuiltApiVersions.add("V1beta1");
        preBuiltApiVersions.add("V1alpha1");
        preBuiltApiVersions.add("V1");
        preBuiltApiVersions.add("V2");
    }

    private static Class<?> loadClass(String name, ClassLoader classLoader) {
        try {
            return Class.forName(name, false, classLoader);
        } catch (ClassNotFoundException e) {
            return null;
        }
    }

posted @ 2024-03-14 11:04  NewSea  阅读(22)  评论(0编辑  收藏  举报