Skip to content

Commit

Permalink
Enhance model loader to support fetching pmcd from workspaces (#2437)
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelbey authored Nov 6, 2023
1 parent 050cb60 commit c63841b
Show file tree
Hide file tree
Showing 18 changed files with 574 additions and 502 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@
<name>Legend Engine - Language Pure - Model Manager - SDLC</name>

<dependencies>
<dependency>
<groupId>org.finos.legend.shared</groupId>
<artifactId>legend-shared-pac4j-gitlab</artifactId>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- ENGINE -->
<dependency>
<groupId>org.finos.legend.engine</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2023 Goldman Sachs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package org.finos.legend.engine.language.pure.modelManager.sdlc;

import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.util.GlobalTracer;
import java.util.List;
import java.util.function.Function;
import javax.security.auth.Subject;
import org.apache.http.impl.client.CloseableHttpClient;
import org.eclipse.collections.api.list.MutableList;
import org.eclipse.collections.impl.utility.ListIterate;
import org.finos.legend.engine.language.pure.modelManager.sdlc.alloy.AlloySDLCLoader;
import org.finos.legend.engine.language.pure.modelManager.sdlc.pure.PureServerLoader;
import org.finos.legend.engine.language.pure.modelManager.sdlc.workspace.WorkspaceSDLCLoader;
import org.finos.legend.engine.protocol.pure.v1.model.context.AlloySDLC;
import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData;
import org.finos.legend.engine.protocol.pure.v1.model.context.PureSDLC;
import org.finos.legend.engine.protocol.pure.v1.model.context.SDLCVisitor;
import org.finos.legend.engine.protocol.pure.v1.model.context.WorkspaceSDLC;
import org.finos.legend.engine.shared.core.kerberos.SubjectTools;
import org.finos.legend.engine.shared.core.operational.errorManagement.EngineException;
import org.pac4j.core.profile.CommonProfile;

final class SDLCFetcher implements SDLCVisitor<PureModelContextData>
{
private final Span parentSpan;
private final String clientVersion;
private final Function<MutableList<CommonProfile>, CloseableHttpClient> httpClientProvider;
private final MutableList<CommonProfile> pm;
private final PureServerLoader pureLoader;
private final AlloySDLCLoader alloyLoader;
private final WorkspaceSDLCLoader workspaceLoader;

public SDLCFetcher(Span parentSpan, String clientVersion, Function<MutableList<CommonProfile>, CloseableHttpClient> httpClientProvider, MutableList<CommonProfile> pm, PureServerLoader pureLoader, AlloySDLCLoader alloyLoader, WorkspaceSDLCLoader workspaceLoader)
{
this.parentSpan = parentSpan;
this.clientVersion = clientVersion;
this.httpClientProvider = httpClientProvider;
this.pm = pm;
this.pureLoader = pureLoader;
this.alloyLoader = alloyLoader;
this.workspaceLoader = workspaceLoader;
}

@Override
public PureModelContextData visit(AlloySDLC sdlc)
{
parentSpan.setTag("sdlc", "alloy");
try (Scope ignore = GlobalTracer.get().buildSpan("Request Alloy Metadata").startActive(true))
{
PureModelContextData loadedProject = this.alloyLoader.loadAlloyProject(pm, sdlc, clientVersion, this.httpClientProvider);
loadedProject.origin.sdlcInfo.packageableElementPointers = sdlc.packageableElementPointers;
List<String> missingPaths = this.alloyLoader.checkAllPathsExist(loadedProject, sdlc);
if (missingPaths.isEmpty())
{
return loadedProject;
}
else
{
throw new EngineException("The following entities:" + missingPaths + " do not exist in the project data loaded from the metadata server. " +
"Please make sure the corresponding Gitlab pipeline for version " + (this.alloyLoader.isLatestRevision(sdlc) ? "latest" : sdlc.version) + " has completed and also metadata server has updated with corresponding entities " +
"by confirming the data returned from <a href=\"" + this.alloyLoader.getMetaDataApiUrl(pm, sdlc, clientVersion) + "\"/> this API </a>.");
}
}
}

@Override
public PureModelContextData visit(PureSDLC pureSDLC)
{
parentSpan.setTag("sdlc", "pure");
try (Scope ignore = GlobalTracer.get().buildSpan("Request Pure Metadata").startActive(true))
{
Subject subject = SubjectTools.getCurrentSubject();

return ListIterate.injectInto(
new PureModelContextData.Builder(),
pureSDLC.packageableElementPointers,
(builder, pointers) -> builder.withPureModelContextData(this.pureLoader.loadPurePackageableElementPointer(pm, pointers, clientVersion, subject == null ? "" : "?auth=kerberos", pureSDLC.overrideUrl))
).distinct().sorted().build();
}
}

@Override
public PureModelContextData visit(WorkspaceSDLC sdlc)
{
parentSpan.setTag("sdlc", "workspace");
parentSpan.setTag("project", sdlc.project);
parentSpan.setTag("workspace", sdlc.getWorkspace());
parentSpan.setTag("isGroupWorkspace", sdlc.isGroupWorkspace);

try (Scope scope = GlobalTracer.get().buildSpan("Request Workspace Metadata").startActive(true))
{
PureModelContextData loadedProject = this.workspaceLoader.loadWorkspace(pm, sdlc, this.httpClientProvider);
PureModelContextData sdlcDependenciesPMCD = this.workspaceLoader.getSDLCDependenciesPMCD(pm, this.clientVersion, sdlc, this.httpClientProvider);
return loadedProject.combine(sdlcDependenciesPMCD);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
import io.opentracing.Span;
import io.opentracing.tag.Tags;
import io.opentracing.util.GlobalTracer;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.security.auth.Subject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
Expand All @@ -31,12 +36,12 @@
import org.eclipse.collections.api.list.MutableList;
import org.eclipse.collections.api.set.primitive.IntSet;
import org.eclipse.collections.impl.factory.primitive.IntSets;
import org.eclipse.collections.impl.utility.ListIterate;
import org.finos.legend.engine.language.pure.modelManager.ModelLoader;
import org.finos.legend.engine.language.pure.modelManager.ModelManager;
import org.finos.legend.engine.language.pure.modelManager.sdlc.alloy.AlloySDLCLoader;
import org.finos.legend.engine.language.pure.modelManager.sdlc.configuration.MetaDataServerConfiguration;
import org.finos.legend.engine.language.pure.modelManager.sdlc.pure.PureServerLoader;
import org.finos.legend.engine.language.pure.modelManager.sdlc.workspace.WorkspaceSDLCLoader;
import org.finos.legend.engine.protocol.pure.v1.model.context.AlloySDLC;
import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContext;
import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData;
Expand All @@ -54,13 +59,6 @@
import org.pac4j.core.profile.CommonProfile;
import org.slf4j.Logger;

import javax.security.auth.Subject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;

import static io.opentracing.propagation.Format.Builtin.HTTP_HEADERS;
import static org.finos.legend.engine.shared.core.kerberos.ExecSubject.exec;

Expand All @@ -72,6 +70,7 @@ public class SDLCLoader implements ModelLoader
private final Supplier<Subject> subjectProvider;
private final PureServerLoader pureLoader;
private final AlloySDLCLoader alloyLoader;
private final WorkspaceSDLCLoader workspaceLoader;
private final Function<MutableList<CommonProfile>, CloseableHttpClient> httpClientProvider;

public SDLCLoader(MetaDataServerConfiguration metaDataServerConfiguration, Supplier<Subject> subjectProvider)
Expand All @@ -91,18 +90,21 @@ public SDLCLoader(MetaDataServerConfiguration metaDataServerConfiguration, Suppl

public SDLCLoader(MetaDataServerConfiguration metaDataServerConfiguration, Supplier<Subject> subjectProvider, PureServerLoader pureLoader, Function<MutableList<CommonProfile>, CloseableHttpClient> httpClientProvider)
{
this.subjectProvider = subjectProvider;
this.pureLoader = pureLoader;
this.alloyLoader = new AlloySDLCLoader(metaDataServerConfiguration);
this.httpClientProvider = httpClientProvider;
this(metaDataServerConfiguration, subjectProvider, pureLoader, httpClientProvider, new AlloySDLCLoader(metaDataServerConfiguration));
}

public SDLCLoader(MetaDataServerConfiguration metaDataServerConfiguration, Supplier<Subject> subjectProvider, PureServerLoader pureLoader, Function<MutableList<CommonProfile>, CloseableHttpClient> httpClientProvider, AlloySDLCLoader alloyLoader)
{
this(subjectProvider, pureLoader, httpClientProvider, alloyLoader, new WorkspaceSDLCLoader(metaDataServerConfiguration.sdlc));
}

public SDLCLoader(Supplier<Subject> subjectProvider, PureServerLoader pureLoader, Function<MutableList<CommonProfile>, CloseableHttpClient> httpClientProvider, AlloySDLCLoader alloyLoader, WorkspaceSDLCLoader workspaceLoader)
{
this.subjectProvider = subjectProvider;
this.pureLoader = pureLoader;
this.alloyLoader = alloyLoader;
this.httpClientProvider = httpClientProvider;
this.workspaceLoader = workspaceLoader;
}

private Subject getSubject()
Expand All @@ -121,6 +123,7 @@ private Subject getSubject()
@Override
public void setModelManager(ModelManager modelManager)
{
this.workspaceLoader.setModelManager(modelManager);
}

@Override
Expand Down Expand Up @@ -170,55 +173,18 @@ public PureModelContextData load(MutableList<CommonProfile> pm, PureModelContext
PureModelContextPointer context = (PureModelContextPointer) ctx;
Assert.assertTrue(clientVersion != null, () -> "Client version should be set when pulling metadata from the metadata repository");

Function0<PureModelContextData> fetchMetadata;

final Subject subject = getSubject();

if (context.sdlcInfo instanceof PureSDLC)
{
fetchMetadata = () ->
{
parentSpan.setTag("sdlc", "pure");
try (Scope scope = GlobalTracer.get().buildSpan("Request Pure Metadata").startActive(true))
{
return ListIterate.injectInto(
new PureModelContextData.Builder(),
context.sdlcInfo.packageableElementPointers,
(builder, pointers) -> builder.withPureModelContextData(this.pureLoader.loadPurePackageableElementPointer(pm, pointers, clientVersion, subject == null ? "" : "?auth=kerberos", ((PureSDLC) context.sdlcInfo).overrideUrl))
).distinct().sorted().build();
}
};
}
else if (context.sdlcInfo instanceof AlloySDLC)
{
fetchMetadata = () ->
{
parentSpan.setTag("sdlc", "alloy");
try (Scope scope = GlobalTracer.get().buildSpan("Request Alloy Metadata").startActive(true))
{
AlloySDLC sdlc = (AlloySDLC) context.sdlcInfo;
PureModelContextData loadedProject = this.alloyLoader.loadAlloyProject(pm, sdlc, clientVersion, this.httpClientProvider);
loadedProject.origin.sdlcInfo.packageableElementPointers = sdlc.packageableElementPointers;
List<String> missingPaths = this.alloyLoader.checkAllPathsExist(loadedProject, sdlc);
if (missingPaths.isEmpty())
{
return loadedProject;
}
else
{
throw new EngineException("The following entities:" + missingPaths + " do not exist in the project data loaded from the metadata server. " +
"Please make sure the corresponding Gitlab pipeline for version " + (this.alloyLoader.isLatestRevision(sdlc) ? "latest" : sdlc.version) + " has completed and also metadata server has updated with corresponding entities " +
"by confirming the data returned from <a href=\"" + this.alloyLoader.getMetaDataApiUrl(pm, sdlc, clientVersion) + "\"/> this API </a>.");
}
}
};
}
else
{
throw new UnsupportedOperationException("To Code");
}
SDLCFetcher fetcher = new SDLCFetcher(
parentSpan,
clientVersion,
this.httpClientProvider,
pm,
this.pureLoader,
this.alloyLoader,
this.workspaceLoader
);

PureModelContextData metaData = subject == null ? fetchMetadata.value() : exec(subject, fetchMetadata::value);
Subject subject = getSubject();
PureModelContextData metaData = subject == null ? context.sdlcInfo.accept(fetcher) : exec(subject, () -> context.sdlcInfo.accept(fetcher));

if (metaData.origin != null)
{
Expand Down Expand Up @@ -314,7 +280,7 @@ public static PureModelContextData loadMetadataFromHTTPURL(MutableList<CommonPro
HttpStatus.SC_GATEWAY_TIMEOUT
);

private static HttpEntity execHttpRequest(Span span, CloseableHttpClient client, HttpRequestBase httpRequest) throws Exception
public static HttpEntity execHttpRequest(Span span, CloseableHttpClient client, HttpRequestBase httpRequest) throws Exception
{
int statusCode = -1;
CloseableHttpResponse response = null;
Expand Down Expand Up @@ -347,7 +313,7 @@ private static HttpEntity execHttpRequest(Span span, CloseableHttpClient client,

if (statusCode < 200 || statusCode >= 300)
{
String msg = EntityUtils.toString(entity);
String msg = entity != null ? EntityUtils.toString(entity) : response.getStatusLine().getReasonPhrase();
response.close();
throw new EngineException("Error response from " + httpRequest.getURI() + ", HTTP" + statusCode + "\n" + msg);
}
Expand Down
Loading

0 comments on commit c63841b

Please sign in to comment.