Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Jenkins Linting unterschiedliche Ergebnisse Python und C#
nieselfriem
myCSharp.de - Member



Dabei seit:
Beiträge: 45

Themenstarter:

Jenkins Linting unterschiedliche Ergebnisse Python und C#

beantworten | zitieren | melden

Hallo Zusammen,

wir haben uns in Python einen Linter zusammengebaut um unsere Jenkinspipline vorab quasi über die API (https://www.jenkins.io/doc/book/pipeline/development/) auf die Richtigkeit der Syntax checken zu lassen. Das funktioniert soweit ganz gut. Nun dachte ich mir, versuchst du das ganze mal etwas komfortabler zu machen und schreibst ein kleines Tool in C# mit einer GUI. Das ging auch recht gut von der Hand.
Leider kommt die API vom Jenkins zu einem unterschiedlichen Ergebnis. Sende ich den Inhalt des Pipelinescripts über Python mit dem unteren Code, kommt zurück, dass der Code valide ist. Dies entspricht auch der Tatsache. Versuche ich das gleiche mit meinem C#-Client, meldet er Syntaxfehler, die nicht existieren.

Dazu hier mal der pythoncode

 with open(file, 'r') as jenkinsFile:
                    jenkinsfile_payload = {'jenkinsfile': jenkinsFile.read()}
                try:
                    r = requests.get(self.jenkins_base_url + '/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,\":\",//crumb)',
                                     auth=HTTPBasicAuth(user, password)).text

                    crumb = r.split(':')
                    crumb_headers = {crumb[0]: crumb[1]}

                    validate = requests.post(self.jenkins_base_url + '/pipeline-model-converter/validate', headers=crumb_headers,
                                             auth=HTTPBasicAuth(user, password), data=jenkinsfile_payload)
                    validation_info = "Die Datei {} wurde geprüft: \n{}".format(file, validate.text)
                    self.outputTextField.setText(validation_info)

meine Adaption dessen in C#:


public string validate(string jenkinsBaseUrl, string[] crumb)
        {
            string url = jenkinsBaseUrl + "/pipeline-model-converter/validate";
            string content = "jenkinsfile="+pipelineContent;
            String responseString;
            var httpRequest = (HttpWebRequest)WebRequest.Create(url);
            var data = Encoding.UTF8.GetBytes(content);
            String base64String = Convert.ToBase64String(Encoding.UTF8.GetBytes("user:password"));          
            httpRequest.Headers.Add("Authorization", "Basic " + base64String);
            httpRequest.Headers.Add(crumb[0], crumb[1]);
            httpRequest.ContentType = "application/x-www-form-urlencoded";
            //httpRequest.ContentType = "text/plain";
            httpRequest.Method = "POST";
            httpRequest.ContentLength = data.Length;
            using (var requestStream = httpRequest.GetRequestStream())
            {
                requestStream.Write(data, 0, data.Length);
            }
            var httpResponse = (HttpWebResponse)httpRequest.GetResponse();
            Console.WriteLine(httpResponse.StatusCode);
            using (Stream stream = httpResponse.GetResponseStream())
            {
                StreamReader reader = new StreamReader(stream, Encoding.UTF8);
                responseString = reader.ReadToEnd();
            }
            Console.WriteLine(responseString);
            return "";
        }

Das Ergbnis wenn ich die API über C# anspreche

"Errors encountered validating Jenkinsfile:\nWorkflowScript: 137: expecting '}', found '' @ line 137, column 43.\n           deployStage == 'prod' \n                                 ^\n\n"

Wieso kommt die API vom Jenkins zu einem anderen Ergebnis, bzw. an welcher Stellschraube könnte ich drehen? Die Variante der Lineendings der Scriptdatei spielt beim Ergebnis offenbar keine Rolle

VG niesel
private Nachricht | Beiträge des Benutzers
Papst
myCSharp.de - Experte



Dabei seit:
Beiträge: 394
Herkunft: Kassel

beantworten | zitieren | melden

Der Unterschied zwischen dem Python Script und dem C# Aufruf, zumindest was mir so auffällt:
  • Bei Python machst du erst einen Get Request, danach den Post.
  • Bei Python sendest du einen POST Request mit JSON Payload; bei C# einen Form Encoded Post Request mit JSON Payload

Verwende für HttpRequests besser den HttpClient (Api-Docs) anstatt des WebClient, der gilt seit einigen Jahren als depreceated.
private Nachricht | Beiträge des Benutzers
T-Virus
myCSharp.de - Member



Dabei seit:
Beiträge: 1900
Herkunft: Nordhausen, Nörten-Hardenberg

beantworten | zitieren | melden

Du solltest dann auch den HttpClient disposen.
Ebenfalls auch deinen StreamReader.
ContentType sollte dann "application/json" sein.
Am besten wäre es auch, wenn du die Konsolenausgaben entfernst.
Wenn du auf falsche Codes reagieren willst, kannst du im HttpClient mit EnsureSuccessStatusCode eine Exception auslösen lassen.

Ebenfalls solltest du auf Rückgabewerte ala "" verzichten und dafür String.Empty nutzen.
Das ist sprechender und erzeugt keine unnötigen Literale.
Strings kansnt du auch durch folgendes Kosntrukt erstellen, was diese lesbarer macht.


string str1 = "Bla"
string str2 = "Blub"
string result = $"{str1} {str2}"; // "Bla Blub"

T-Virus
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von T-Virus am .
Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16147

beantworten | zitieren | melden

Zitat von T-Virus
Du solltest dann auch den HttpClient disposen.
Leider ist das gar keine gute Idee.
HTTP Client ist eines der ganz wenigen, wenn nicht gar einziges Beispiel in .NET, das nicht disposed werden sollte; es ist sogar kontraproduktiv den HttpClient zu disposen.
https://www.stevejgordon.co.uk/httpclient-creation-and-disposal-internals-should-i-dispose-of-httpclient
Der bessere Weg wäre aber die HttpClientFactory. Aus Logiksicht macht das aber keinen Unterschied.
Beim StreamReader gilt das nicht, der sollte disposed werden.

WebRequest ist die schlechteste Art und Weise, wie man in .NET einen Web Request machen kann, daher ist das Ding seit Jahren obsolete.
Modern kann man eine API prinzipiell mit Refit ansprechen, sodass man null Verwaltungsaufwand hat und sich alles generieren lassen kann, seit Refit 6 mit Source Code Generators.
Damit wären alle Code Issues hier (zB. vermutlich auch Encoding und Content Type) auch behoben (abgesehen von den Logik-Unterschieden zum Python Script).
Zitat von T-Virus
Ebenfalls solltest du auf Rückgabewerte ala "" verzichten und dafür String.Empty nutzen.
Sorry, das ist totaler Quatsch :-) Und hat leider auch gar nichts mit dem Problem zutun; genauso wie das Console-Zeug vermutlich dem Debugging geschuldet ist und er das sicher weiß.
Technisch gesehen gibt absolut keinen Unterschied zwischen "" und string.Empty, das ist dem ILCode auch zu entnehmen.
Die Konstante ("") hat sogar den Vorteil, dass man das überall verwenden kann - string.Empty nicht. Man sollte aber lieber string statt String als Typ verwenden, um Kollisionen zu vermeiden.
Zitat
string stringEmpty = String.Empty;
00007FFD4C390FBB mov rcx,1DC38553060h
00007FFD4C390FC5 mov rcx,qword ptr [rcx]
00007FFD4C390FC8 mov qword ptr [rbp+30h],rcx

string stringConstant = "";
00007FFD4C390FCC mov rcx,1DC38553060h
00007FFD4C390FD6 mov rcx,qword ptr [rcx]
00007FFD4C390FD9 mov qword ptr [rbp+28h],rcx
Bitte die Basics dazu verstehen, zB hat Nick Chapsas gibts ein YouTube Video, damit dieser Mythos irgendwann hoffentlich verschwindet :-)
Technisch gesehen also Geschmackssache, was man verwenden will; gibts nichts zu kritisieren.
Is string.Empty actually better than "" in C#?

Bitte beachten zumindest Grundlegend beim Problem des Themas zu bleiben.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
T-Virus
myCSharp.de - Member



Dabei seit:
Beiträge: 1900
Herkunft: Nordhausen, Nörten-Hardenberg

beantworten | zitieren | melden

@Abt
Hast Recht, wird auch in der Doku angedeutet bzw. soll man die Instanz wiederverwenden.
War mir leider nicht bewusst, wird auch nicht direkt als Hinweis/Warnung in der Doku angezeigt.
Danke

T-Virus
Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.
private Nachricht | Beiträge des Benutzers
nieselfriem
myCSharp.de - Member



Dabei seit:
Beiträge: 45

Themenstarter:

beantworten | zitieren | melden

Hallo zusammen,

In der C# Implementierung habe ich den GET-Request weg gelassen. Dieser holt nur eine ID vom Jenins ab und das funktioniert auch soweit. Mein Problem ist tatsächlich nur der POST-Request. Für den Rest der Antworten muss ich mich noch kurz belesen.

VG niesel
private Nachricht | Beiträge des Benutzers
JimStark
myCSharp.de - Member

Avatar #dOpLzh7hN1az1g0eGRc0.jpg


Dabei seit:
Beiträge: 293

beantworten | zitieren | melden

Du sendest im Python-Teil das Dictionary als Payload:


jenkinsfile_payload = {'jenkinsfile': jenkinsFile.read()}

Das sollte das gleiche Format als String ergeben.

Bei .NET:


string content = "jenkinsfile="+pipelineContent;

Vielleicht solltest du da mal testen ob du es auch als JSON übergeben kannst.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von JimStark am .
private Nachricht | Beiträge des Benutzers
nieselfriem
myCSharp.de - Member



Dabei seit:
Beiträge: 45

Themenstarter:

beantworten | zitieren | melden

In Python wird das nicht als Json übergeben. Das ist ein Dictionary. Ich glaube das äquivalent in C# ist eine Map. Bin mir da aber nicht sicher. Ich bin unter .Net bisher sehr selten unterwegs.
private Nachricht | Beiträge des Benutzers
Papst
myCSharp.de - Experte



Dabei seit:
Beiträge: 394
Herkunft: Kassel

beantworten | zitieren | melden

Am einfachsten du vergleichst die Requests und schaust dir den Unterschied an. Ein Dictionary gibt es auch in .NET.

Mit dem HttpClient sieht das ganze in C# dann auch nicht mehr so aufwendig aus, in grob etwa so:


var cl = new HttpClient();
cl.BaseAddress = new Uri("https://jenkins");
cl.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "base64encoded");

var validationResult = await cl.PostAsync("/pipeline-model-converter/validate", new FormUrlEncodedContent(new Dictionary<string?, string?>()
{
	["jenkinsfile"] = "jenkinscontent"
}));

string result = await validationResult.Content.ReadAsStringAsync();

Edit: stimmt, mit dem json hatte ich mich in meinem Eingangsposting vertan.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Papst am .
private Nachricht | Beiträge des Benutzers