2013-10-22 2 views
1

저는 자동화 기술자이며 자동화 테스트를 위해 Jenkins를 사용합니다. 여러 플랫폼에서 각 테스트를 테스트해야하므로 빌드에 이러한 매개 변수가있을 수 있습니다.Jenkins Dynamic Locking

OS (윈도우 7, 윈도우 8, XP 64 비트, XP 32 비트, 등)

서버 (우리의 제품의 서버, 버전 X 버전 y를, 등등 ...)

제품 버전 (X, Y 등)

그리고 더 ...

선택된 OS는 VM이 ​​(가상 머신) 시험 근거로 사용될지를 결정.

문제는 많은 테스트가 있고 테스트를 실행하는 사용자가 이미 사용중인 VM을 확인하지 못하거나 특정 VM을 사용하여 다른 자동 테스트 시간에 자동 테스트를 설정하지 않는 경우입니다.

빌드를 사용하여 VM을 사용할 때까지 기다려야합니다.

잠금 및 래치 플러그인으로 재생하려고했는데, 빌드 매개 변수에 이름이 있으면 각 잠금을 확인하도록 플러그인을 변경하고, 있으면 잠금 값을 확인하십시오. 따라서 잠금 이름이 "OS 유형"이고 빌드에 "OS 유형 = Windows 7"매개 변수가 있으면 빌드가 잠금 "Windows 7"을 검색하여 자유 상태인지 아닌지 확인합니다.

위의 부분을 처리했지만 테스트를 실행할 때 첫 번째 테스트는 환경을 구축하고 다른 테스트는 잠금을 확인하지 않고 전체 빌드를 완료 할 때까지 기다립니다. 덕분에, 나는 내가 한 일이 무엇인지 모른다.

아무도 도와 줄 수 있습니까? 누구든지 그런 짓을 했니? 아래 코드를 게시 하겠지만, 내가 말했듯이 의도 한대로 작동하는지 확실하지 않습니다. 미리 감사드립니다.

public class LockWrapper extends BuildWrapper implements ResourceActivity { 
private List<LockWaitConfig> locks; 

public LockWrapper(List<LockWaitConfig> locks) { 
    for(LockWaitConfig lock : locks) 
    { 

    } 
    this.locks = locks; 
} 

public List<LockWaitConfig> getLocks() { 
    return locks; 
} 

public void setLocks(List<LockWaitConfig> locks) { 
    this.locks = locks; 
} 

@Override 
public Descriptor<BuildWrapper> getDescriptor() { 
    return DESCRIPTOR; 
} 

@Extension 
public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); 

/** 
* @see ResourceActivity#getResourceList() 
*/ 
public ResourceList getResourceList() { 
    ResourceList resources = new ResourceList(); 
    for (LockWaitConfig lock : locks) { 
     resources.w(new Resource(null, "dynamic-locks/" + lock.getName(), DESCRIPTOR.getWriteLockCount())); 
    } 
    return resources; 
} 

@Override 
public Environment setUp(AbstractBuild abstractBuild, Launcher launcher, BuildListener buildListener) throws IOException, InterruptedException { 
    final List<NamedReentrantLock> backups = new ArrayList<NamedReentrantLock>(); 
    List<LockWaitConfig> locks = new ArrayList<LockWaitConfig>(this.locks); 

    // sort this list of locks so that we _always_ ask for the locks in order 
    Collections.sort(locks, new Comparator<LockWaitConfig>() { 
     public int compare(LockWaitConfig o1, LockWaitConfig o2) { 
      return o1.getName().compareTo(o2.getName()); 
     } 
    }); 

    // build the list of "real" locks 
    for (LockWaitConfig lock : locks) { 
     NamedReentrantLock backupLock; 
     String varName = lock.getName(); 
     String temp = varName; 
     if(abstractBuild.getBuildVariables().containsKey(varName)) 
     { 
      temp = abstractBuild.getBuildVariables().get(varName).toString(); 
      buildListener.getLogger().println("Variable " + varName + " found, replacing it with the value '" + temp + "'"); 
     } 
     do { 
      backupLock = DESCRIPTOR.backupLocks.get(temp); 
      if (backupLock == null) { 
       DESCRIPTOR.backupLocks.putIfAbsent(temp, new NamedReentrantLock(temp)); 
      } 
     } while (backupLock == null); 
     backups.add(backupLock); 
    } 

    final StringBuilder locksToGet = new StringBuilder(); 
    CollectionUtils.forAllDo(backups, new Closure() { 
     public void execute(Object input) { 
      locksToGet.append(((NamedReentrantLock) input).getName()).append(", "); 
     } 
    }); 

    buildListener.getLogger().println("[Dynamic Locks] Locks to get: " + locksToGet.substring(0, locksToGet.length()-2)); 

    boolean haveAll = false; 
    while (!haveAll) { 
     haveAll = true; 
     List<NamedReentrantLock> locked = new ArrayList<NamedReentrantLock>(); 

     DESCRIPTOR.lockingLock.lock(); 
     try { 
      for (NamedReentrantLock lock : backups) { 
       buildListener.getLogger().print("[Dynamic Locks] Trying to get " + lock.getName() + "... "); 
       if (lock.tryLock()) { 
        buildListener.getLogger().println(" Success"); 
        locked.add(lock); 
       } else { 
        buildListener.getLogger().println(" Failed, releasing all locks"); 
        haveAll = false; 
        break; 
       } 
      } 
      if (!haveAll) { 
       // release them all 
       for (ReentrantLock lock : locked) { 
        lock.unlock(); 
       } 
      } 
     } finally { 
      DESCRIPTOR.lockingLock.unlock(); 
     } 

     if (!haveAll) { 
      buildListener.getLogger().println("[Dynamic Locks] Could not get all the locks, sleeping for 1 minute..."); 
      TimeUnit.SECONDS.sleep(60); 
     } 
    } 

    buildListener.getLogger().println("[Dynamic Locks] Have all the locks, build can start"); 

    return new Environment() { 
     @Override 
     public boolean tearDown(AbstractBuild abstractBuild, BuildListener buildListener) throws IOException, InterruptedException { 
      buildListener.getLogger().println("[Dynamic Locks] Releasing all the locks"); 
      for (ReentrantLock lock : backups) { 
       lock.unlock(); 
      } 
      buildListener.getLogger().println("[Dynamic Locks] All the locks released"); 
      return super.tearDown(abstractBuild, buildListener); 
     } 
    }; 
} 

public void makeBuildVariables(AbstractBuild build, Map<String,String> variables) { 
    final StringBuilder names = new StringBuilder(); 
    for (LockWaitConfig lock : locks) { 
     if (names.length() > 0) { 
      names.append(','); 
     } 
     names.append(lock.getName()); 
    } 
    variables.put("LOCKS", names.toString()); 
} 

public String getDisplayName() { 
    return DESCRIPTOR.getDisplayName(); 
} 

public static final class DescriptorImpl extends Descriptor<BuildWrapper> { 
    private List<LockConfig> locks; 

    /** 
    * Required to work around HUDSON-2450. 
    */ 
    private transient ConcurrentMap<String, NamedReentrantLock> backupLocks = 
      new ConcurrentHashMap<String, NamedReentrantLock>(); 

    /** 
    * Used to guarantee exclusivity when a build tries to get all its locks. 
    */ 
    private transient ReentrantLock lockingLock = new ReentrantLock(); 

    DescriptorImpl() { 
     super(LockWrapper.class); 
     load(); 
    } 

    public String getDisplayName() { 
     return "Locks"; 
    } 


    @Override 
    public BuildWrapper newInstance(StaplerRequest req, JSONObject formData) throws FormException { 
     List<LockWaitConfig> locks = req.bindParametersToList(LockWaitConfig.class, "locks.locks."); 
     return new LockWrapper(locks); 
    } 

    @Override 
    public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { 
     req.bindParameters(this, "locks."); 
     locks = req.bindParametersToList(LockConfig.class, "locks.lock."); 
     save(); 
     return super.configure(req, formData); 
    } 

    @Override 
    public synchronized void save() { 
     // let's remove blank locks 
     CollectionUtils.filter(getLocks(), new Predicate() { 
      public boolean evaluate(Object object) { 
       return StringUtils.isNotBlank(((LockConfig) object).getName()); 
      } 
     }); 

     // now, we can safely sort remaining locks 
     Collections.sort(this.locks, new Comparator<LockConfig>() { 
      public int compare(LockConfig lock1, LockConfig lock2) { 
       return lock1.getName().compareToIgnoreCase(lock2.getName()); 
      } 
     }); 

     super.save(); 
    } 

    public List<LockConfig> getLocks() { 
     if (locks == null) { 
      locks = new ArrayList<LockConfig>(); 
      // provide default if we have none 
      locks.add(new LockConfig("(default)")); 
     } 
     return locks; 
    } 

    public void setLocks(List<LockConfig> locks) { 
     this.locks = locks; 
    } 

    public LockConfig getLock(String name) { 
     for (LockConfig host : locks) { 
      if (name.equals(host.getName())) { 
       return host; 
      } 
     } 
     return null; 
    } 

    public String[] getLockNames() { 
     getLocks(); 
     String[] result = new String[locks.size()]; 
     for (int i = 0; i < result.length; i++) { 
      result[i] = locks.get(i).getName(); 
     } 
     return result; 
    } 

    public void addLock(LockConfig hostConfig) { 
     locks.add(hostConfig); 
     save(); 
    } 

    /** 
    * There wass a bug in the ResourceList.isCollidingWith, 
    * this method used to determine the hack workaround if the bug is not fixed, but now only needs to 
    * return 1. 
    */ 
    synchronized int getWriteLockCount() { 
     return 1; 
    } 
} 

public static final class LockConfig implements Serializable { 
    private String name; 
    private transient AbstractBuild owner = null; 

    public LockConfig() { 
    } 

    @DataBoundConstructor 
    public LockConfig(String name) { 
     this.name = name; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    @Override 
    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (o == null || getClass() != o.getClass()) return false; 

     LockConfig that = (LockConfig) o; 

     if (name != null ? !name.equals(that.name) : that.name != null) return false; 

     return true; 
    } 

    @Override 
    public int hashCode() { 
     int result; 
     result = (name != null ? name.hashCode() : 0); 
     return result; 
    } 
} 

public static final class LockWaitConfig implements Serializable { 
    private String name; 
    private transient LockConfig lock; 

    public LockWaitConfig() { 
    } 

    @DataBoundConstructor 
    public LockWaitConfig(String name) { 
     this.name = name; 
    } 

    public LockConfig getLock() { 
     if (lock == null && name != null && !"".equals(name)) { 
      setLock(DESCRIPTOR.getLock(name)); 
     } 
     return lock; 
    } 

    public void setLock(LockConfig lock) { 
     this.lock = lock; 
    } 

    public String getName() { 
     if (lock == null) { 
      return name; 
     } 
     return name = lock.getName(); 
    } 

    public void setName(String name) { 
     setLock(DESCRIPTOR.getLock(this.name = name)); 
    } 

} 

/** 
* Extends {@code ReentrantLock} to add a {@link #name} attribute (mainly 
* for display purposes). 
*/ 
public static final class NamedReentrantLock extends ReentrantLock { 
    private String name; 

    public NamedReentrantLock(String name) { 
     this.name = name; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 
} 

private static final Logger LOGGER = Logger.getLogger(LockWrapper.class.getName()); 

} 

기본적으로 변경된 사항은 다음과 같습니다. 또 다른 예제를 명확히하기 위해

for (LockWaitConfig lock : locks) { 
     NamedReentrantLock backupLock; 
     String varName = lock.getName(); 
     String temp = varName; 
     if(abstractBuild.getBuildVariables().containsKey(varName)) 
     { 
      temp = abstractBuild.getBuildVariables().get(varName).toString(); 
      buildListener.getLogger().println("Variable " + varName + " found, replacing it with the value '" + temp + "'"); 
     } 
     do { 
      backupLock = DESCRIPTOR.backupLocks.get(temp); 
      if (backupLock == null) { 
       DESCRIPTOR.backupLocks.putIfAbsent(temp, new NamedReentrantLock(temp)); 
      } 
     } while (backupLock == null); 
     backups.add(backupLock); 
    } 

내가 (테스트 환경)

를 들어 동일한 자원을 가질 수있다 다른 작업을 실행하려고

(이를 데리고 당신에게 피터 Schuetze 감사) 이 예제에서는 두 가지 다른 작업을 수행합니다.

작업 A는 내가 선택한 모든 VM에서 일부 테스트를 실행합니다.

작업 B는 내가 선택한 모든 VM에서 다른 테스트를 실행합니다.

작업 A를 VM 'Windows 7'에서 실행하도록 선택하고 다른 사람이 작업 A를 실행 한 후에 VM 'Windows 7'에서 작업 B를 실행하려고하면 작업 A가 완료 될 때까지 작업 B를 차단하고 싶습니다. .

각각 다른 VM에서 작동하도록 설정된 많은 작업 A 및 작업 B 변형이있을 수 있지만 플랫폼 매트릭스를 고려하면 처리하기가 너무 어려울 수 있습니다.

잠금 플러그인을 사용하지 않으려면 테스트 목록이 다음과 같이 표시됩니다.

  • 작업 A - 윈도우 7 - 서버 A
  • 작업 A - 윈도우 7 - 서버 B
  • 작업 A - 윈도우 8 - 서버 A
  • 작업 A - 윈도우 8 - 서버 B
  • 작업 A - 윈도우 XP의 64 - 서버 A
  • 작업 A - 윈도우 XP 64 - 서버 B
  • 작업 A - 윈도우 XP의 86 - 서버 A
  • 작업 A - 윈도우 XP 86 - 서버 B
  • 작업 B - 윈도우 7 - 서버 A
  • 작업 B - 윈도우 7 - 서버 B
  • 작업 B - 윈도우 8 - 서버 A
  • 작업 B - 윈도우 8 - 서버 B
  • 작업 B - 윈도우 XP의 64 - 서버 A
  • 작업 B - 윈도우 XP 64 - 서버 B
  • 작업 B - 윈도우 XP의 86 - 서버 A
  • 작업 B - 윈도우 XP 86 - 서버 B
  • ,

현실에있는 것을 고려하시기 바랍니다 나는이 ... 약 20 일을 지금, 각각 사용하여 더 많은 또는 동일한 자원 (테스트 환경, 서버, 등 ...) 지금 내가 만든

이하 그래서 내 일 목록은 그와 같습니다.

  • 작업 A를
      - $ 서버 - - $ 변수가 - $가 Another_Variable
    • 작업 B - $ OS_TYPE - $ 서버 - $가 변수 - $ OS_TYPE $ Another_Variable

    그리고는지 확인하려면 어떤 자원이 없다 둘 이상의 작업에서 동시에 사용되는 경우 잠금 플러그인이 필요하며 변수로 변수를 받아 들여야합니다. 당신은 더 이상 질문이나 설명이 필요가있는 경우

    , 그냥 정리 해보에 :

  • 답변

    1

    문의 해 주시기 바랍니다. 동적 라벨을 허용하도록 로그 및 래치 플러그인을 수정했습니다. 이제 동일한 작업으로 다른 테스트를 실행 해보십시오.

    동시에 실행되도록 작업을 구성 했습니까? 이 설정을 사용하면 동일한 작업의 인스턴스를 병렬로 실행할 수 있습니다.


    BTW,이 멀티 구성 프로젝트에 대한 일반적인 사용 사례처럼 들린다.

    +0

    한 가지를 제외하고는 요점이 정확합니다. 동일한 리소스 (테스트 환경)를 가질 수있는 다른 작업을 실행하려고합니다. 원본 게시물의 예를 통해 명확하게 설명하겠습니다. 정리해 줘서 고마워. –