【问题标题】:JClouds-Chef: IllegalAccessError on TokenType2?JClouds-Chef:TokenType2 上的 IllegalAccessError?
【发布时间】:2014-09-06 18:19:01
【问题描述】:

我正在使用 JClouds-Chef API 通过 Chef Client 引导 Linux VM (myapp01.me.example.com),然后运行客户端并通过 typicalapp 角色使用其应用程序堆栈配置 VM:

package com.me.myorg.chef;

import org.jclouds.Constants
import org.jclouds.ContextBuilder
import org.jclouds.chef.ChefContext
import org.jclouds.chef.ChefService
import org.jclouds.chef.config.ChefProperties
import org.jclouds.chef.domain.BootstrapConfig
import org.jclouds.chef.util.RunListBuilder
import org.jclouds.compute.domain.ExecResponse
import org.jclouds.compute.domain.OsFamily
import org.jclouds.domain.LoginCredentials
import org.jclouds.scriptbuilder.domain.Statement
import org.jclouds.ssh.SshClient
import org.jclouds.sshj.config.SshjSshClientModule

import com.google.common.base.Charsets
import com.google.common.collect.ImmutableSet
import com.google.common.io.Files
import com.google.common.net.HostAndPort
import com.google.inject.Key
import com.google.inject.TypeLiteral

public class ChefProvisioner {
    public static void main(String[] args) {
        ChefProvisioner.provision()
    }

    public static provision() {
        String vmIp = "myapp01.me.example.com";     // A Linux VM living in our local vCenter
        String vmSshUsername = "admin";
        String vmSshPassword = "12345";

        String endpoint = "https://mychefserver";
        String client = "myuser";
        String validator = "chef-validator";
        String clientCredential = Files.toString(new File("C:\\Users\\myuser\\sandbox\\chef\\myuser.pem"), Charsets.UTF_8);
        String validatorCredential = Files.toString(new File("C:\\Users\\myuser\\sandbox\\chef\\chef-validator.pem"), Charsets.UTF_8);

        Properties props = new Properties();
        props.put(ChefProperties.CHEF_VALIDATOR_NAME, validator);
        props.put(ChefProperties.CHEF_VALIDATOR_CREDENTIAL, validatorCredential);
        props.put(Constants.PROPERTY_RELAX_HOSTNAME, "true");
        props.put(Constants.PROPERTY_TRUST_ALL_CERTS, "true");

        ChefContext ctx = ContextBuilder.newBuilder("chef")
            .endpoint(endpoint)
            .credentials(client, clientCredential)
            .overrides(props)
            .modules(ImmutableSet.of(new SshjSshClientModule())) //
            .buildView(ChefContext.class);
        ChefService chef = ctx.getChefService();

        List<String> runlist = new RunListBuilder().addRole("typicalapp").build();
        BootstrapConfig bootstrapConfig = BootstrapConfig.builder().runlist(runlist).build();

        chef.updateBootstrapConfigForGroup("jclouds-chef", bootstrapConfig);
        Statement bootstrap = chef.createBootstrapScriptForGroup("jclouds-chef");

        SshClient.Factory sshFactory = ctx.unwrap().utils()
            .injector().getInstance(Key.get(new TypeLiteral<SshClient.Factory>() {}));
        SshClient ssh = sshFactory.create(HostAndPort.fromParts(vmIp, 22),
        LoginCredentials.builder().user(vmSshUsername).password(vmSshPassword).build());

        ssh.connect();
        try {
            String rawScript = bootstrap.render(OsFamily.UNIX);
            ExecResponse result = ssh.exec(rawScript);
        } finally {
            ssh.disconnect();
        }
    }
}

当我运行它时,我得到:

Exception in thread "main" java.util.ServiceConfigurationError: org.jclouds.apis.ApiMetadata: Provider org.jclouds.openstack.swift.SwiftApiMetadata could not be instantiated: java.lang.IllegalAccessError: tried to access class com.google.common.reflect.TypeResolver from class org.jclouds.util.TypeToken2
    at java.util.ServiceLoader.fail(ServiceLoader.java:224)
    at java.util.ServiceLoader.access$100(ServiceLoader.java:181)
    at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:377)
    at java.util.ServiceLoader$1.next(ServiceLoader.java:445)
    at com.google.common.collect.ImmutableCollection$Builder.addAll(ImmutableCollection.java:323)
    at com.google.common.collect.ImmutableSet$Builder.addAll(ImmutableSet.java:633)
    at org.jclouds.apis.Apis.all(Apis.java:72)
    at org.jclouds.apis.Apis.withId(Apis.java:89)
    at org.jclouds.ContextBuilder.newBuilder(ContextBuilder.java:168)
    at org.jclouds.ContextBuilder$newBuilder.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
    at com.me.myorg.chef.ChefProvisioner.provision(ChefProvisioner.groovy:51)
    at com.me.myorg.chef.ChefProvisioner$provision.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
    at com.me.myorg.chef.ChefProvisioner.main(ChefProvisioner.groovy:27)
Caused by: java.lang.IllegalAccessError: tried to access class com.google.common.reflect.TypeResolver from class org.jclouds.util.TypeToken2
    at org.jclouds.util.TypeToken2.where(TypeToken2.java:47)
    at org.jclouds.rest.internal.BaseRestApiMetadata.contextToken(BaseRestApiMetadata.java:60)
    at org.jclouds.rest.internal.BaseRestApiMetadata$Builder.<init>(BaseRestApiMetadata.java:74)
    at org.jclouds.openstack.swift.SwiftApiMetadata$Builder.<init>(SwiftApiMetadata.java:85)
    at org.jclouds.openstack.swift.SwiftApiMetadata$Builder.<init>(SwiftApiMetadata.java:81)
    at org.jclouds.openstack.swift.SwiftApiMetadata$ConcreteBuilder.<init>(SwiftApiMetadata.java:108)
    at org.jclouds.openstack.swift.SwiftApiMetadata$ConcreteBuilder.<init>(SwiftApiMetadata.java:108)
    at org.jclouds.openstack.swift.SwiftApiMetadata.<init>(SwiftApiMetadata.java:60)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at java.lang.Class.newInstance(Class.java:374)
    at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:373)
    ... 16 more

关于这里发生了什么的任何想法?

【问题讨论】:

    标签: java chef-infra jclouds


    【解决方案1】:

    为了能够引导您的虚拟机,您将需要以下内容:

    • VM 的 IP 地址。它必须是可访问的,打开端口 22(因为引导程序将通过 SSH 完成)并且可以访问 Internet,以便它可以安装 Ruby 和 Chef。
    • Chef 服务器中的客户端及其对应的私钥。 jclouds 使用该客户端对 Chef Server REST API 执行操作。
    • 验证器证书。该证书将上传到节点,以便它可以在 Chef 服务器中自行注册。

    在这种情况下,由于没有使用 jclouds ComputeService,您必须手动实例化一个 SSH 客户端才能连接到虚拟机,但这应该非常简单。

    在您提到的示例中,使用 ChefSolo 时克隆了 Git 存储库,但由于您拥有 Chef 服务器,因此您唯一需要做的就是配置连接以及所需的运行列表和属性。

    执行此操作的最小程序需要以下依赖项:

    <!-- Required dependencies -->
    <dependency>
        <groupId>org.apache.jclouds.driver</groupId>
        <artifactId>jclouds-sshj</artifactId>
        <version>${jclouds.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.jclouds.api</groupId>
        <artifactId>chef</artifactId>
        <version>${jclouds.version}</version>
    </dependency>
    

    它可能是这样的:

    更新:我已更改配置以匹配提供的 knife.rb 配置,还添加了几个属性以避免 SSL 错误,因为您的 Chef 端点是https。

    // Configuration
    String vmIp = "vm-ip";
    String vmSshUsername = "root";
    String vmSshPassword = "foo";
    
    String endpoint = "https://mychefserver.example.com";
    String client = "myuser";
    String validator = "chef-validator";
    String clientCredential = Files.toString(new File("/home/myuser/.chef/myuser.pem"), Charsets.UTF_8);
    String validatorCredential = Files.toString(new File("/home/myuser/.chef/chef-validator.pem"), Charsets.UTF_8);
    
    Properties props = new Properties();
    props.put(ChefProperties.CHEF_VALIDATOR_NAME, validator);
    props.put(ChefProperties.CHEF_VALIDATOR_CREDENTIAL, validatorCredential);
    props.put(Constants.PROPERTY_RELAX_HOSTNAME, "true");
    props.put(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
    
    /* *** First, create the context to connect to the Chef Server *** */
    
    // Create the context and configure the SSH driver to use. sshj in this example
    ChefContext ctx = ContextBuilder.newBuilder("chef")
        .endpoint(endpoint)
        .credentials(client, clientCredential)
        .overrides(props)
        .modules(ImmutableSet.of(new SshjSshClientModule())) //
        .buildView(ChefContext.class);
    CherService chef = ctx.getChefService();
    
    /* *** Second, generate the bootstrap script *** */
    
    // Generate the bootsrap configuration
    List<String> runlist = new RunListBuilder().addRole("typicalapp").build();
    BootstrapConfig bootstrapConfig = BootstrapConfig.builder().runlist(runlist).build();
    
    // Generate the bootstrap script to be executed in the VM (this will persist
    // the configuration in a data bag under the key 'jclouds-chef' so it can be reused
    // and then build the bootstrap script with the information in the configuration data bag)
    chef.updateBootstrapConfigForGroup("jclouds-chef", bootstrapConfig);
    Statement bootstrap = chef.createBootstrapScriptForGroup("jclouds-chef");
    
    /* *** Finally create an SSH connection manually and run the script on the VM *** */
    
    SshClient.Factory sshFactory = ctx.unwrap().utils()
        .injector().getInstance(Key.get(new TypeLiteral<SshClient.Factory>() {}));
    SshClient ssh = sshFactory.create(HostAndPort.fromParts(vmIp, 22),
        LoginCredentials.builder().user(vmSshUsername).password(vmSshPassword).build());
    
    ssh.connect();
    try {
        String rawScript = bootstrap.render(OsFamily.UNIX);
        ExecResponse result = ssh.exec(rawScript);
    } finally {
        ssh.disconnect();
    }
    

    【讨论】:

    • (1) 你应该可以使用VM的FQDN,只要你运行jclouds程序的DNS可以解析它。 (2) 是的,client 是 Chef Server 中必须存在的客户端。 Chef 中的客户是“api 消费者”。 (3) 验证器是所有 Chef Server 安装中的“特殊客户端”,knife-bootstrap 使用它在第一次运行时在 Chef Server 中注册节点。 jclouds-chef 和knife-bootstrap 做的一样。
    • 是的,这些是客户。对于验证器,有“something-validator”。您需要使用该名称及其私钥。如果您没有将它存储在任何地方,您可以从 Chef Server UI 重新生成它。对于客户端,同样的,那里会有另一个“客户端”。你必须使用它的名字和它的私钥。如果您使用托管 Chef,则客户端和验证器前缀通常是您的组织名称。
    • 没有。当引导程序完成时,您将看到一个与您的机器同名的新节点和客户端,但在此之前,客户端必须存在。 Chef 中的客户是“api 用户”。当你使用 Knife 时,客户端和验证者的名字取自 Knife.rb 配置文件。这些是刀用来做事情的“用户”(客户端和验证器); jclouds-chef 也一样。你必须使用这些。
    • 没有。 %machine_name% 不是客户端名称。 jclouds-chef 会自动为你生成一个 %machine_name% 。当您使用knife 时,它​​会在内部从knife.rb 文件中读取配置(它通常位于/etc/chef 或当前目录的.chef/ 文件夹中)。除了其他配置之外,该配置文件还包含以下属性:node_nameclient_keyvalidation_client_namevalidation_key。这些值是您必须用于客户端和验证器名称和密钥的值。
    • 我刚刚编辑了示例程序并更改了配置以匹配 knife.rb 文件中提供的配置,还添加了几个属性以确保没有 SSL 问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-03
    • 2021-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多